Le polymorphisme est-il possible sans héritage?

Dans une interview, on m'a demandé si le polymorphisme peut être atteint sans héritage. Est-ce possible?

46
demandé sur user2864740 2012-07-31 06:49:12

6 réponses

La meilleure explication sur le sujet que j'ai jamais lu est un article de Luca Cardelli, un théoricien de type renommé. L'article est nommé sur la compréhension des Types, L'Abstraction de données et le polymorphisme .

Types de polymorphisme

Cardelli définit plusieurs types de polymorphisme dans cet article:

  • universel
    • paramétrique
    • inclusion
  • Ad-hoc
    • surcharge
    • coercition

Le type de polymorphisme lié à l'héritage est classé comme polymorphisme d'inclusion ou polymorphisme de sous-type.

Wikipédia fournit une bonne définition:

En programmation orientée objet, polymorphisme de sous-type ou inclusion le polymorphisme est un concept dans la théorie des types, dans lequel un nom peut désigner instances de nombreuses classes différentes tant qu'elles sont liées par une super classe commune. Le polymorphisme d'Inclusion est généralement pris en charge par le sous-typage, c'est-à-dire que les objets de différents types sont entièrement substituable pour des objets d'un autre type (leur base type (S)) et peut donc être géré via une interface commune. Alternativement, le polymorphisme d'inclusion peut être réalisé par type coercition, également connu sous le nom de coulée de type.

Un autre article de Wikipedia appelé polymorphisme dans la programmation orientée objet semble également répondre à vos questions.

En Java

Ce sous-type fonctionnalité en Java est atteint, entre autres moyens, grâce à l'héritage des classes et des interfaces. Bien que les fonctionnalités de sous-typage de Java peuvent ne pas être évidentes en termes d'héritage tout le temps. Prenons par exemple les cas de covariance et de contravariance avec les génériques. En outre, les tableaux sont sérialisables et Clonables bien que cela ne soit évident nulle part dans la hiérarchie des types. On peut aussi dire que grâce à la conversion d'élargissement primitive, les opérateurs numériques en Java sont polymorphes, dans certains cas même accepter des opérandes totalement indépendants (c'est-à-dire concaténation de chaînes et de nombres ou d'une chaîne plus un autre objet). Considérez également les cas de boxe et de déballage des primitives. Ces derniers cas de polymorphisme (coercions et surcharge) ne sont pas du tout liés à l'héritage.

Exemples

L'Inclusion

List<Integer> myInts = new ArrayList<Integer>();

C'est le cas auquel votre question semble se référer, c'est-à-dire lorsqu'il existe une relation d'héritage ou d'implémentation entre le types, comme dans ce cas où ArrayList implémente List.

Comme je l'ai mentionné, cependant, lorsque vous introduisez des génériques Java, les règles de sous-typage deviennent floues:

List<? super Number> myObjs = new ArrayList<Object>();
List<? extends Number> myNumbers = new LinkedList<Integer>();

Et dans d'autres cas, les relations ne sont même pas évidentes dans L'API

Cloneable clone = new int[10];
Serializable obj = new Object[10]

Pourtant, tout cela, selon Cardelli, sont des formes de polymorphisme universel.

Paramétrique

public <T> List<T> filter(Predicate<T> predicate, List<T> source) {
  List<T> result = new ArrayList<>();
  for(T item : source) {
    if(predicate.evaluate(item)){
         result.add(item);
    }
   return result;
  }
}

Le même algorithme peut être utilisé pour filtrer toutes sortes de listes avec toutes sortes de prédicats sans avoir à répéter une seule ligne de code pour chaque type de liste. Le type de la liste et le type de prédicat sont paramétrique. Voir cet exemple avec des expressions lambda disponibles dans JDK 8 Preview (pour la concision de l'implémentation du prédicat).

filter(x -> x % 2 == 0, asList(1,2,3,4,5,6)); //filters even integers
filter(x -> x % 2 != 0, asList(1L,2L,3L,4L,5L,6L)); //filters odd longs
filter(x -> x >= 0.0, asList(-1.0, 1.0)); //filters positive doubles

Selon Cardelli, c'est une forme de polymorphisme universel.

La Contrainte

double sum = 1 + 2.0;

Entier et arithmétique à virgule flottante sont totalement différents. Application de l'opérateur plus à deux opérandes de types différents, ici, est impossible sans une certaine forme de coercition.

Dans cet exemple, les types integer et double, sont automatiquement contraints (convertis) en type double sans conversion explicite. L'expression entière est promue à double. En effet, en Java, nous avons des conversions d'élargissement primitives.

Selon Cardelli, cette forme de coercition automatique est une forme de polymorphisme ad hoc prévu pour l'opérateur plus.

Il y a des langues dans que vous ne pourriez même pas additionner un entier et un nombre à virgule flottante sans un cast explicite (C'est-à-dire AFAIK, SML, dans lequel, en passant, le polymorphisme paramétrique est la clé pour surmonter ce genre de problèmes).

La Surcharge

double sum = 2.0 + 3.0;
String text = "The sum is" + sum;

L'opérateur plus signifie ici deux choses différentes en fonction des arguments utilisés. De toute évidence, l'opérateur a été surchargé. Cela implique qu'il a différentes implémentations en fonction des types d'opérandes. Selon Cardelli, il s'agit d'un forme de polymorphisme ad hoc prévue pour l'opérateur plus.

Ceci, bien sûr, s'applique également aux formes de surcharge de méthode dans les classes (c'est-à-dire java.lang.Les méthodes mathématiques min et max sont surchargées pour supporter différents types primitifs).

Dans D'Autres Langues

Même lorsque l'héritage joue un rôle important dans la mise en œuvre de certaines de ces formes de polymorphisme, ce n'est certainement pas le seul moyen. D'autres langages qui ne sont pas orientés objet fournissent d'autres formes de polymorphisme. Prenons, par exemple, les cas de Duck typing dans des langages dynamiques comme Python ou même dans des langages statiquement typés comme Go, ou types de données algébriques dans des langages comme SML, Ocaml et Scala, ou classes de type dans des langages comme Haskell, multi methods dans Clojure, héritage prototypique en JavaScript, etc.

57
répondu Edwin Dalorzo 2016-11-30 13:20:15

Polymorphisme ad hoc > surcharge de L'opérateur > sans héritage

Polymorphisme ad hoc > surcharge de méthode > sans héritage

Polymorphisme ad hoc > substitution de méthode > avec Inheritence

Polymorphisme Paramétrique > Génériques > Sans Héritage

Polymorphisme de sous-type ou polymorphisme D'Inclusion > affectation polymorphe > avec héritage

Polymorphisme de sous-type ou polymorphisme D'Inclusion > Type de retour polymorphe > avec L'héritage

Polymorphisme de sous-type ou polymorphisme D'Inclusion > Type D'Argument polymorphe > avec héritage

Polymorphisme de coercition > élargissement > avec ou sans héritage

Polymorphisme de coercition > auto boxing et unboxing > sans héritage

Polymorphisme de coercition > var args > sans héritage

Polymorphisme De Coercition > Type Casting > Sans Héritage

6
répondu Amitabha Roy 2013-04-07 18:12:22

Bien sûr. En Java, deux classes peuvent implémenter la même interface et leurs résultats sont polymorphes. Aucune fonctionnalité n'est héritée.

public interface Foo {
  public int a();
}

public class A implements Foo {
  public int a() {
    return 5;
  }
}


public class B implements Foo {
  public int a() {
    return 6;
  }
}

, Puis ailleurs:

Foo x = new A();
System.out.println(x.a())
Foo y = new B();
System.out.println(y.a())

Deux x et y sont Foos, mais ils ont des résultats différents lorsque vous appelez a().

5
répondu Max 2012-07-31 03:01:54

Type Statique

Surcharge - ce qui signifie plusieurs méthodes avec le même nom mais une signature différente qui est possible sans surcharger

class StaticPolyExample
{
void print(int s)
{
    //print s
}

void print(String s)
{
    //print s
}

}

Type dynamique

Overriding - ce qui signifie que la méthode dans la super classe sera redéfinie dans la sous-classe qui a besoin d'héritage

class Printer
{
void print(String s)
{
  // prints String
}

}

class diffPrinter extends Printer
{
void print(String s)
{
  // prints String differently
}

}
0
répondu Arun 2012-07-31 02:58:31

La surcharge de fonction est l'un des polymorphismes (bien que ce ne soit pas ce que l'on entend par polymorphisme réel) qui peut être atteint sans héritage.

Par exemple

class Foo { 
public void Arrest( Animal A){
    /*code...*/
 }  
public void Arrest( Terrorist T ) {
    /*code...*/
 }  

}


from main :

Foo f= new Foo();
f.Arrest( new Lion() );
f.Arrest(new Terrorist());

Arrestation méthode est appelée 2 fois, mais le chemin d'exécution du code est différent.

* encore une fois, ce n'est pas une véritable forme de polymorphisme. Le polymorphisme réel en général ne peut pas être atteint sans héritage.

0
répondu Dhananjay 2012-07-31 15:17:54

Oui, je pense qu'ils voulaient probablement entendre parler de polymorphisme par les interfaces. Donc, s'il y a 2 classes qui implémentent à partir de la même interface, alors nous pouvons utiliser dans tous les endroits où nous exspectons un objet avec un tel intervace. Voir le code de wikipedia:

// from file Animal.java

public interface Animal {
        public String talk();
}

// from file Cat.java

public class Cat implements Animal {
        @Override
        public String talk() {
                return "Cat says Meow!";
        }
}

// from file Dog.java

public class Dog implements Animal {
        @Override
        public String talk() {
                return "Dog says Woof! Woof!";
        }
}

// from file PolymorphismExample.java

public class PolymorphismExample {

        public static void main(String[] args) {
                Collection<Animal> animals = new ArrayList<Animal>();
                animals.add(new Cat());
                animals.add(new Dog());

                for (Animal a : animals) {
                        System.out.println(a.talk());
                }
        }

}
0
répondu Danil Speransky 2012-09-12 04:44:52