19

I'm following this:

http://rickyclarkson.blogspot.com/2006/07/duck-typing-in-java-and-no-reflection.html

And I'm trying to adapt this:

<T extends CanQuack & CanWalk> void doDucklikeThings(T t)
{
    t.quack();
    t.walk();
}

To this:

public class Activate<D extends CanQuack & CanWalk> {

    D d = new MyWaterFowl(); //Type mismatch
}

Even though MyWaterFowl implements those interfaces.

I'd like a solution that never mentions MyWaterFowl in the <>'s since I'm going to eventually just be injecting it (or anything else that implements those interfaces).


If your answer is basically "You can't do that, what type would it even be?". Please explain why it's working for the method doDucklikeThings and conclusively state if it is impossible to do the same with a class or, if it is possible, how to do it.

The T in doDucklikeThings must be something valid since it's working. If I passed that into a class what would I be passing in?


As requested here's the MyWaterFowl code:

package singleResponsibilityPrinciple;

interface CanWalk { public void walk(); }
interface CanQuack { public void quack(); }
interface CanSwim { public void swim(); }


public class MyWaterFowl implements CanWalk, CanQuack, CanSwim {

    public void walk() { System.out.println("I'm walkin` here!"); }
    public void quack() { System.out.println("Quack!"); }
    public void swim() { System.out.println("Stroke! Stroke! Stroke!"); }
}

Remember I've confirmed that doDucklikeThings works. I need the syntax that will let me inject anything that implements the required interfaces.

candied_orange
  • 7,036
  • 2
  • 28
  • 62
  • We don't know what a class type declaration of `MyWaterFowl` looks like. We don't know if it extends `CanQuack` or implements `CanWalk`. – Makoto May 22 '14 at 05:46
  • I'd like to comment on what you are trying to do in the second snippet: I don't know your reasons but it seems to be against the very idea of generics. You define a generic type and then you go ahead and force an implementation on top of it. You only do that in the second snippet, and I believe this is not (and should not be) possible. I'm not positive about it though, so I'm not making this an answer. But I think you should re-think the way you are trying to solve the problem here. – Yekhezkel Yovel May 22 '14 at 14:46
  • http://docs.oracle.com/javase/tutorial/java/generics/restrictions.html#createObjects you can't instantiate a generic type but there is a workaround – Harry Blargle May 22 '14 at 21:05

6 Answers6

27

This does not work, because the class/method is generic and the caller of your class/method can set D to MyAmericanEagle.

 Activate<MyAmericanEagle> active = new Activate<>();

Then your code would result in

 MyAmericanEagle d = new MyWaterFowl(); 

Since that makes no sense (would result in ClassCastException) the compiler rejects it.

Thilo
  • 257,207
  • 101
  • 511
  • 656
12

// Type mismatch

Even though MyWaterFowl implements those interfaces.

It's not about the type D implementing those interfaces (and/or extending a class). A generic type variable is bound to a specific type argument. That type might be completely different than MyWaterFowl so you cannot use them interchangeably.


To answer your edit, you are doing two completely different things in your two snippets. The type variable is declared with some bounds. It is therefore guaranteed to be a type that implements some interface (or extends some class), but you don't know which type that is, in either case.


I want to clarify the two things you did, ie. what you expect in your question and the solution you gave in your answer.

Generics are a compile time feature where the server code, for example

class Activate<D extends CanWalk & CanQuack> {
    D instance;
    public Activate(D d) {
        this.instance = d;
    }

    public D getInstance() {
        return instance ;
    }
}

declares a type variable. This is a variable. Within its declaration context, you don't know its exact type at compile time.

The client code, for example,

new Activate<>(new MyWaterFowl());

binds the type MyWaterFowl to the type variable declared in Activate. So the client code knows what D is at compile time.

If the following

public D getInstance() {
    D someRef = new MyWaterFowl();
    return someRef;
}

was allowed in the server code, this would fail

Activate<SomeOtherBird> activate = new Activate<>(new SomeOtherBird());
SomeOtherBird reference = activate.getInstance();

Generics guarantee that getInstance() is type safe because it is declared as returning whatever type is bound to the type variable D. In this case, that is SomeOtherBird. If the getInstance() code above was allowed, type safety would be broken as getInstance() would return something other than what was bound to it.

This doesn't change the fact that within your server code (the generic class), you do know the bounds of D, ie. it is both a CanQuack and a CanWalk. Therefore, anything an object of those types can do, so can an object referenced by a D variable do.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • Right, I'm trying not to know. I just want to know it supports those two interfaces. Remember, DoDucklikeThings works. So there is some compiler construct that represents what I'm after. I need the syntax that works for a class or a good explanation why classes work differently then methods. – candied_orange May 22 '14 at 06:09
  • 1
    @CandiedOrange As I said, it works but it is completely unrelated to your second example. You know that `T` is a `CanQuack` so you can invoke its method on a value of type `T`. But you don't know what the type `T` is really, so you can't assign some specific type to a variable of type `T`, which is what you are doing in your second example. – Sotirios Delimanolis May 22 '14 at 06:12
7

It actually can be done.

The generic code between <>'s is fine. It's not different for methods and classes. Just needed to finish doing the dependency injection:

public class Activate<D extends CanQuack & CanWalk> {
    private D d;

    Activate(D d) {
        this.d = d;
    }

    void doDuckLikeThings() {
        d.quack();
        d.walk();
        //d.swim(); //Doesn't work 
        //And shouldn't since that was the whole point of ISP.
    }
}

public class MainClass {
    public static void main(String[] args) {
       Activate<MyWaterFowl> a = new Activate<>(new MyWaterFowl());
       a.doDuckLikeThings();
    }
}

Thought I'd provide an answer that says what to do to fix it.

candied_orange
  • 7,036
  • 2
  • 28
  • 62
  • 1
    i just tried the same, and this works fine, but i think, the real question is: how should we do that, if someone instantiate Activate with a no arg constructor, then the no arg constructor should instantiate D, whatever it is (only that it should implement canquack and canqalk).. how can we refer to the type written into the diamonds? – AdamK May 22 '14 at 08:25
  • Since D is a type, not an instance, for that to work you'd be requiring that type to also have a default constructor. I'm not sure that can be made to work. If you don't care what they put in Activate<> and seriously just want some default behavior when they don't bother to send you an instance then you CAN cast an instance to type D from inside Activate. It works but I think you'll confuse people. – candied_orange May 22 '14 at 08:40
  • could you edit your answer with showing that (only commented)? – AdamK May 22 '14 at 08:44
  • You mean the cast for default behavior option that I'm not recommending? – candied_orange May 22 '14 at 08:47
  • Lets take that question here: http://codereview.stackexchange.com/questions/51387/if-i-ignore-generic-type-to-provide-default-behavor-when-client-uses-my-default – candied_orange May 22 '14 at 09:19
2

Type of class which extends Object is different with type of Object. This means you cannot instantiate super class for its extenders.

When you compile this:

1    public class Sample<T extends Object> {
2      public Sample() {
3         T t = new Object();
4       }
5    }

you got incompatible types error.

Sample.java:3: error: incompatible types
    T t = new Object();
          ^
  required: T
  found:    Object
  where T is a type-variable:
    T extends Object declared in class Sample
1 error

Its same as when you do it in non generic form:

1 public class Sample{
2    public Sample() {
3    }
4 }
5
6 class SampleExtends extends Sample {
7   public SampleExtends() {
8
9    }
10 }
11
12 class User {
13    public User() {
14        SampleExtends se = new Sample();
15    }
16 }

you get this error from compiler: incompatible types

Sample.java:14: error: incompatible types
    SampleExtends se = new Sample();
                       ^
  required: SampleExtends
  found:    Sample
1 error
Amir Naghizadeh
  • 383
  • 3
  • 14
1

What do you expect the actual type of D to be in the second code snippet?

Let's say something does this:

Activate<Daffy> myActivate = Activate<Daffy>();

What should happen then? This means D needs to be of type Daffy but you are trying to set d to an instance of MyWaterFowl.

WW.
  • 23,793
  • 13
  • 94
  • 121
1

Generics works just the same for classes as for methods.

You seem to misunderstand Generics a bit. A type parameter <T extends CanQuack & CanWalk> is not a definition of a new type alias valid for the scope of method or class, it is a placeholder for some type, which is later filled by someone else.

For a generic method like your example

<T extends CanQuack & CanWalk> void doDucklikeThings(T t)
{
    t.quack();
    t.walk();
}

the caller of the method decides which type to substitute for T (within the limits stated by the bounds). So one can use

doDuckLikeThings(myWaterfowl);

and the compiler guesses what type parameter you want here (likely the type of the myWaterfowl variable).

For a generic class, it is the code which creates an instance of the class who decides the actual type parameter. So I can say

Activate<Duck> duckActivator = new Activate<Duck>();

After this, the duckActivator instance is an instance of Activate<Duck>, and inside it, D is now bound to Duck. Now obviously your variable declaration says

Duck d = new MyWaterfowl();

and this doesn't work.

It wouldn't work in a generic method, either – this one is invalid just as well:

<T extends CanQuack & CanWalk> void doDucklikeThings()
{
    T t = new MyWaterfowl();
}

I'd like a solution that never mentions MyWaterFowl in the <>'s since I'm going to eventually just be injecting it (or anything else that implements those interfaces).

Then don't mention its constructor either. Whoever is injecting your duck like object also has to decide the actual type parameter – i.e. this should be the one who creates the Activate instance. And this can't change after construction of your Activate object.

A candidate solution to the actual problem

If this restriction doesn't fit for you (e.g. because the same instance of Activate will need to be able to work with different types of duck like objects one after another), maybe a type parameter is not for you.

Maybe a generic wrapper with a wildcard type parameter for a variable helps instead.

Take this generic wrapper (or something similar):

public class Holder<T> {
    private final T t;
    public T get() { return t; }
    public Holder(T t) { this.t = t; }
}

Then your Activate class doesn't need the type parameter, but just holds a holder variable with a wildcard type parameter. (Note that the actual Holder instantiation has a non-wildcard type argument.)

public class Activate {
    private Holder<? extends CanQuack & CanWalk> holder;

    public <D extends CanQuack & CanWalk> void setDuckLike(D d) {
        this.holder = new Holder<D>(d);
    }

    public Activate() {}

    public void doDucklikeThings() {
         holder.get().quack();
         holder.get().walk();
    }
}

Now you do something like this:

Activate a = new Activate();
a.setDuckLike(new Duck());
a.doDucklikeThings();
a.setDuckLike(new MyWaterfowl());
a.doDucklikeThings();
Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
  • Not sure what the generic wrapper buys us, other then a chance to showoff the wildcard type parameter, but thanks for the though explanation and +1 for providing an answer with a solution rather than just stating that it won't work. – candied_orange May 23 '14 at 01:06
  • You can't use a pure wildcard type for a variable (i.e. something like `CanQuack & CanWalk duckLike;` is invalid), you can only use them in type parameters. Thus this allows us to emulate the type alias, i.e. using different object fitting the type restrictions. – Paŭlo Ebermann May 23 '14 at 11:51
  • simpler solution and no indirections: `private getDuck(){(D)return duck;}`. No need for indirection, it creates extra clutter and cache misses. – bestsss May 23 '14 at 12:40
  • @bestsss and `duck` has which type? A cast with a generic type gives you a compiler warning, and no actual type safety. This is like using a `void*` in C for a variable. – Paŭlo Ebermann May 23 '14 at 18:24
  • Object or CanQuack would suffice (as that would be the bytecode's declaration). I would not care less about compiler warnings (hi "@SuppressWarnings") as implementing the simplest array-backer collection requires that just the same (or Node based with sentinels). And if one can not keep private members proper policy, there is no chance to do much. The extra clutter is just that - extra clutter. Ah no it's not like using void* as java has type-safe checks regadrless the compiler warnings - technically you can implement the calls via `CanQuack.class.cast` lameness and have no warnings. – bestsss May 24 '14 at 10:41