Is polymorphism possible without inheritance?
Asked Answered
V

6

67

In an interview I was asked if polymorphism can be achieved without inheritance. Is this possible?

Vassal answered 31/7, 2012 at 2:49 Comment(0)
C
83

The best explanation on the subject that I've ever read is an article by Luca Cardelli, a renown type theorist. The article is named On Understanding Types, Data Abstraction, and Polymorphism.

Types of Polymorphism

Cardelli defines several types of polymorphism in this article:

  • Universal
  • parametric
  • inclusion
  • Ad-hoc
  • overloading
  • coercion

The kind of polymorphism related to inheritance is classified as inclusion polymorphism or subtype polymorphism.

Wikipedia provides a good definition:

In object-oriented programming, subtype polymorphism or inclusion polymorphism is a concept in type theory wherein a name may denote instances of many different classes as long as they are related by some common super class. Inclusion polymorphism is generally supported through subtyping, i.e., objects of different types are entirely substitutable for objects of another type (their base type(s)) and thus can be handled via a common interface. Alternatively, inclusion polymorphism may be achieved through type coercion, also known as type casting.

Another Wikipedia article called Polymorphism in object-oriented programming seems to answer your questions as well.

In Java

This subtyping feature in Java is achieved, among other means, through the inheritance of classes and interfaces. Although the subtyping features of Java may not be evident in terms of inheritance all the time. Take for example the cases of covariance and contravariance with generics. Also, arrays are Serializable and Cloneable although this is not evident anywhere in the type hierarchy. It can also be said that through primitive widening conversion the numeric operators in Java are polymorphic, in certain cases even accepting totally unrelated operands (i.e. concatenation of strings and numbers or of a string plus some other object). Consider also the cases of boxing and unboxing of primitives. These latter cases of polymorphism (coercion and overloading) are not at all related to inheritance.

Examples

Inclusion

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

This is the case to which your question seems to refer i.e. when there is an inheritance or implementation relationship between the types, as in this case where ArrayList implements List.

As I mentioned, though, when you introduce Java generics, some time the rules of subtyping get fuzzy:

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

And in other cases, the relationships are not even evident in the API

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

Even so, all these, according to Cardelli, are forms of universal polymorphism.

Parametric

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;
  }
}

The same algorithm can be used to filter all kinds of lists with all kinds of predicates without having to repeat a single line of code for every possible type of list. The type of the actual list and the type of predicate are parametric. Like in the following examples with Java lambda expressions.

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

According to Cardelli, this is a form of universal polymorphism.

Coercion

double sum = 1 + 2.0;

Integer and floating-point arithmetic are totally different. Applying the plus operator to two operands of different types here is impossible without some form of coercion.

In this example, the types integer and double, are automatically coerced (converted) to type double without an explicit cast. The entire expression is promoted to double. This is so because in Java we have primitive widening conversions.

According to Cardelli, this form of automatic coercion is a form of ad-hoc polymorphism provided for the plus operator.

There are languages in which you could not even sum an integer and a floating-point number without an explicit cast (i.e. AFAIK, SML, in which, by the way, parametric polymorphism is key to overcome this kind of problems).

Overloading

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

The plus operator here means two different things depending on the arguments used. Evidently, the operator has been overloaded. This implies it has different implementations depending on the types of operands. According to Cardelli, this is a form of ad-hoc polymorphism provided for the plus operator.

This, of course, also applies to forms of method overloading in classes (i.e java.lang.Math methods min and max are overloaded to support different primitive types).

In Other Languages

Even when inheritance plays an important role in the implementation of some of these forms of polymorphism, certainly it is not the only way. Other languages that are not object-oriented provide other forms of polymorphism. Take, for example, the cases of duck typing in dynamic languages like Python or even in statically-typed languages like Go, or algebraic data types in languages like SML, Ocaml and Scala, or type classes in languages like Haskell, multi methods in Clojure, prototypal inheritance in JavaScript, etc.

Candiot answered 31/7, 2012 at 3:13 Comment(4)
Great response. It'd be even better is you were to start it off with a definite and clear summary to answer the question.Rexanne
@RichardSitze I gave it a shot to a more detailed explanation. I hope I have not obscured the meaning of the original post.Candiot
@Edwin Delorzo Wow.. well actually I was trying to suggest was a one-liner at the top to state: Yes or No. The reader then has some idea where they're going as they read the rest. Also, as Stephen C has pointed out, the answer may be different Java (which was the original question), then for other languages. Nice job!Rexanne
yup there are other languages like Ruby, Python, JavaScript, Visual Basic, and Smalltalk take advantage of powerful CPUs to use interpreters to defer type checking to run time (whether the source code is compiled to byte code or purely interpreted). By deferring type checking we break the link between inheritance and polymorphism,Natural
B
8

Ad-hoc Polymorphism > Operator Overloading > Without Inheritence

Ad-hoc Polymorphism > Method Overloading > Without Inheritence

Ad-hoc Polymorphism > Method Overriding > With Inheritence

Parametric Polymorphism > Generics > Without Inheritence

Subtype Polymorphism or Inclusion Polymorphism > Polymorphic Assignment > With Inheritence

Subtype Polymorphism or Inclusion Polymorphism > Polymorphic Return Type > With Inheritence

Subtype Polymorphism or Inclusion Polymorphism > Polymorphic Argument Type > With Inheritence

Coercion Polymorphism > Widening > With or without Inheritence

Coercion Polymorphism > Auto boxing and unboxing > Without Inheritence

Coercion Polymorphism > Var args > Without Inheritence

Coercion Polymorphism > Type Casting > Without Inheritence

Brigid answered 7/4, 2013 at 18:12 Comment(0)
M
4

Sure. In Java, you can have two classes implement the same interface, and their results are polymorphic. No functionality is inherited.

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;
  }
}

Then elsewhere:

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

Both x and y are Foos, but they have different results when you call a().

Magnetron answered 31/7, 2012 at 2:52 Comment(5)
The interface is inherited. The semantic contract (never inforceble) is implicitly inherited. See Edwin's response.Rexanne
Fine, that's fair. I assumed the questioner meant 'implementation inheritance'. Without further clarification from the interviewer, and especially in a Java-oriented interview, this answer is probably what they're looking for. There are, of course, much more detailed answers.Magnetron
Yeah I agree. I tend to think of interfaces (rightly or wrongly) as a way to guarantee to the compiler that an object will have a certain method with a certain signature, which puts me in the mind of duck typing, which doesn't really imply inheritance. Technically it IS considered inheritance, sure.Arrhythmia
@Dhananjay No it doesn't. Inheritance (extension) copies behavior; implementing an interface does not.Cheyenne
Interface is a kind of stateless inheritance.Rinse
A
1

Static type

overloading - which means multiple methods with same name but different signature which is possible with out overriding

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

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

}

Dynamic type

overriding - which means the method in super class will be re-defined in sub class which needs inheritance

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

}

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

}
Albertson answered 31/7, 2012 at 2:58 Comment(0)
D
1

Function overloading is one of the polymorphism (though it's not what real polymorphism is meant) which can be achieved without inheritance.

e.g.

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());

Arrest method is called 2 times but the path of execution of the code is different.

*Again this is not true form of polymorphism. Real polymorphism in General can not be acheived without inheritance.

Doersten answered 31/7, 2012 at 15:17 Comment(2)
Languages that are not object-oriented provide forms of polymorphism which do not rely on inheritance (i.e parametric polymorphism). Some of this is possible in Java through generics.Candiot
@EdwinDalorzo : and what is definition of polymorphism in those languages? it may not be same as in oop.Doersten
T
0

Yes, I think they probably wanted to hear about polymorphism by interfaces. So if there 2 classes which implements from the same interface, then we can use in all places where we exspect an object with such intervace. See code from 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());
                }
        }

}
Trumaine answered 31/7, 2012 at 2:52 Comment(1)
Copied Max's answer down to the mistake where B implements goo instead of foo.Backler

© 2022 - 2024 — McMap. All rights reserved.