0

I have a method as given below that gives me compile time error.

class Parent {
  public abstract <T extends Parent> T copy();
}

and in Child class

class Child extends parent{

public <T extends Parent> T copy() {

               return new Child();
        }

}

By generics I am telling type eraser that I would return child object still it is not letting me do this. Anyone have an opinion/solution on this?

I can solve my problem with new approach as given below.

class Parent {

public Parent copy(){
  //some stuff
}

}

class Child extends Parent{

   @Override
   public Child copy(){
         Child c = (Child)   super.copy();//I want to avoid this type casting here.

}
}

So the above way solves my problem but I have to TYPE CAST in the child if I want to use parent class copy method and additionaly want to do more stuff in Child class.

So considering both, do u have better way of solving problem?

Mohammad Adnan
  • 6,527
  • 6
  • 29
  • 47

4 Answers4

2

The problem is that your generic argument isn't bound to anything when you declare the method this way. The only way the compiler can bind a type for T is in lines where you call the method. For example, it will assume type Child in the following call:

Child  c = copy();

Now, what happens if your code was considered legal by the compiler, and you did something like the following?

GrandChild g = copy();

In this case you'll get a an unexpected ClassCastException at runtime, and this is exactly what generics tries to avoid.

Note that even if you cast to T, as others suggested, you get a compiler warning, because the risk is still there.

The following alternative is better in type safety terms, but a little cumbersome:

abstract class Parent<T extends Parent<T>> {
  public abstract T copy();
}

class Child extends Parent<Child> {
  public Child copy() {
    return new Child();
  }
}
Eyal Schneider
  • 22,166
  • 5
  • 47
  • 78
  • Yeah - I was commenting while you were editing. +1 – Brian Roach Dec 23 '13 at 07:24
  • The this seems to be helpful. But So much generics for small purpose. – Mohammad Adnan Dec 23 '13 at 07:38
  • If this is the only solution i.e. using class level generics for method level thing then I would prefer type casting :) – Mohammad Adnan Dec 23 '13 at 07:41
  • it seems I somewhere require type casting. In Parent copy method I am doing Parent p = this.getClass().newInstance(); but I simply can't return p in parent class. here we have to type casting p as our Parent copy method is returning T. – Mohammad Adnan Dec 23 '13 at 08:06
  • @MohdAdnan: If you want such a default behavior in your parent, then I see 2 choices: 1) Casting 2) Passing Class in the constructor, and using it for creating the instance. This doesn't require a cast, but it requires the artificial constructor argument. Anyway, both solutions will fail if a subclass doesn't respect the convention, and assigns an incorrect type to T. – Eyal Schneider Dec 23 '13 at 08:42
  • that should just be `abstract class Parent` – newacct Dec 24 '13 at 10:21
  • @newacct: The idea is that the artificial generic argument should be identical to the type itself. By adding the Parent bound I restrict the usage and reduce mistakes. – Eyal Schneider Dec 24 '13 at 11:53
  • @EyalSchneider: It does not have any type-safety purpose. Both ways are equally type-safe. It does not restrict the way you think. – newacct Dec 24 '13 at 23:06
  • @newacct: The proposed solution above is not perfect, but it does provide better type safety, compared with the alternative of returning a Parent reference always, and then requiring the caller to cast it to the correct type. Adding the bound is not mandatory, but it makes the intention a little clearer, and prevents the implementor from doing something like "class Child extends Parent". – Eyal Schneider Dec 25 '13 at 06:24
  • @EyalSchneider: "compared with the alternative" I am talking about compared to `abstract class Parent`. "it does provide better type safety" It does not, not for what you are using it for. It does not avoid any more casts than `abstract class Parent`. "it makes the intention a little clearer" But Generics is not about intention. It's about type safety (avoiding casts). "and prevents the implementor from doing something like" But it does not prevent the implementor from doing something like `class SomethingElse extends Parent` – newacct Dec 26 '13 at 03:11
  • @newacct: I understand your point, but I see no problem with making the intention clearer, without changing the bytecode or adding complexity. And more important, consider what happens if the caller holds a reference Parent> p1, which is then copied(using copy method) to Parent> p2. Without any bound, you would have to cast the result back to Parent> in order to work with it as a Parent instance. So actually it does have type safety implication in some use cases. – Eyal Schneider Dec 26 '13 at 07:27
  • @EyalSchneider: If a particular method needs to use that property, then the method can be a generic method, with a bound. No reason to have a bound on the type. – newacct Dec 27 '13 at 05:06
  • @EyalSchneider: Also, in that case the bound should be `>` -- the bound on `Parent` extended does not matter. – newacct Dec 29 '13 at 15:33
1

You don't need Generics for what you want to do.

Java allows for covariant return types:

abstract class Parent {
    protected int i; 

    public Parent(int i) {
        this.i = i;
    }
    public abstract Parent copy();
}

public class Child extends Parent {
    private int j;

    public Child(int i, int j) {
        super(i);
        this.j = j;
    }

    @Override
    public Child copy() {
        return new Child(this.i, this.j);
    }
}

This is specifically covered in Section 8.4.5 of the JLS

Edit in response to OP adding to Q:

The answer is you don't call super.copy(). You're instantiating a new object in your copy() method which means the constructor is involved. The constructor for Child is going to call the constructor for Parent. In the Child's copy() you simply call the Child's constructor. I've edited the above example to show what I mean.

Brian Roach
  • 76,169
  • 12
  • 136
  • 161
  • well let me reframe the question again then. wait for a while plz. – Mohammad Adnan Dec 23 '13 at 07:28
  • Please see the second part of question. you will understand better why I want generics. I want to avoid type casting in child. – Mohammad Adnan Dec 23 '13 at 07:32
  • Hmm you want me to write whole code :) think about any other use case where I don't create objects. but scenario is same. By the way I am creating object in parent class using reflection. – Mohammad Adnan Dec 23 '13 at 07:40
0

Generics allow the programmer to specify what type T is. If the user tries something like

OtherChild child = <OtherChild>copy();

where OtherChild extends Parent, but OtherChild isn't a Child, Java will expect copy() to return a type of T, or OtherChild in this case. Since OtherChild isn't a Child, you get a compile time error. You might want to try casting your return value to T as suggested by Nikolay, or leave this up to the end user and change copy()'s return type to Child.

Edit

If you are implementing an abstract method as in your question, you will need to use the casting method; obviously, changing the method's return type in unacceptable here.

Kyle
  • 374
  • 1
  • 11
-1

You need manually cast result to T, like

    public <T extends Parent> T copy() {

       return (T) new Child();
    }