Using Java and C#'s generics to simulate duck typing

1.1k views Asked by At

http://nullprogram.com/blog/2014/04/01/ tries to explain that Java's generics can't simulate duck typing with an example:

class Caller<T> {
    final T callee;
    Caller(T callee) {
        this.callee = callee;
    }
    public void go() {
        callee.call();  // compiler error: cannot find symbol call
    }
}

class Foo {
    public void call() { System.out.print("Foo"); }
}

class Bar {
    public void call() { System.out.print("Bar"); }
}

public class Main {
    public static void main(String args[]) {
        Caller<Foo> f = new Caller<>(new Foo());
        Caller<Bar> b = new Caller<>(new Bar());
        f.go();
        b.go();
        System.out.println();
    }
}

The program will fail with a compile-time error. This is the result of type erasure. Unlike C++’s templates, there will only ever be one compiled version of Caller, and T will become Object. Since Object has no call() method, compilation fails.

Does it mean that by Java generics, the methods of a type parameter are limited to the methods of class java.lang.Object?

C#'s generics is implemented in terms of reification instead of type erasure. Does C#'s generics not have the above limitation as Java's generics? So can C#'s generics actually achieve the same thing as duck typing?

Thanks.

3

There are 3 answers

5
Peter Duniho On BEST ANSWER

can C#'s generics actually achieve the same thing as duck typing?

No. But C#'s generics can include a constraint where the type parameter is restricted to inherit or implement some particular type. When that's done, any expression of the type of that type parameter is resolved as the constrained type and members from that type can be accessed.

This is similar to the extends constraint described in the article you read.

The only duck-typing support in C# is the dynamic keyword, where final compilation of expressions involving dynamic values is deferred until runtime when the actual runtime type is known.

Related reading:

Trivial C# class with a generic parameter wouldn't compile for no apparent reason
Call a method of type parameter

2
SLaks On

C# has the same limitations.

Other than dynamic, C# does not have arbitrary ducky-typing anywhere; even with generics, you can only call methods as defined by a type (specifically, the constraint(s) for the generic type parameter, which default to object).

7
markspace On

Does it mean that by Java generics, the methods of a type parameter are limited to the methods of class java.lang.Object?

Not exactly. While most generics are erased, it is possible to include a constraint in Java such that the type parameter must be of a certain type. Which effectively makes it not Object.

Untested, but this should be close.

class Caller<T extends CallMe> {
    final T callee;
    Caller(T callee) {
        this.callee = callee;
    }
    public void go() {
        callee.call();  // should work now
    }
}

interface CallMe {
    void call();
}

class Foo implements CallMe {
    public void call() { System.out.print("Foo"); }
}

class Bar implements CallMe {
    public void call() { System.out.print("Bar"); }
}

public class Main {
    public static void main(String args[]) {
        Caller<Foo> f = new Caller<>(new Foo());
        Caller<Bar> b = new Caller<>(new Bar());
        f.go();
        b.go();
        System.out.println();
    }
}