2

I am new to generics and was looking at this answer:

https://stackoverflow.com/a/17165079/1632141

It works perfectly, however I couldn't understand how this part of the code works.

 public T calories(int val) {
     calories = val;
     return (T) this;
 }

in the inner class of the NutritionFacts.

How does the cast work here? I was expecting java.lang.ClassCastException here, since we are casting a pure superclass object to subclass.

Community
  • 1
  • 1
starkk92
  • 5,754
  • 9
  • 43
  • 59
  • 1
    What do you mean, "pure superclass object"? `this` always ends up being of type `T` anyway. – Louis Wasserman Jul 04 '16 at 17:50
  • The code runs in the context of `public static class (GMOFacts.)Builder extends NutritionFacts.Builder<(GMOFacts.)Builder>`. `T` is therefore the builder's own type which makes it a legal cast. Also `this` is not of the superclass type, even though the code is declared in the superclass. `this` is always the concrete runtime (subclass) type. – zapl Jul 04 '16 at 18:04
  • 1
    @LouisWasserman: Not true. It's possible for `this` to not be of type `T`. – newacct Jul 06 '16 at 06:32
  • 1
    @zapl: It's possible for `this` to not be a `T`. – newacct Jul 06 '16 at 06:33

2 Answers2

2

Actually there's a problem with that answer:

public static class Builder<T extends Builder> {...

The type T is a raw type. It should be:

public static class Builder<T extends Builder<T>> {...

which is called a self referencing type, because it's extending itself.

It's typically used like this:

public class SubBuilder extends Builder<SubBuilder> {...

in which case, the type is the type of the subclass itself.

You could code another subclass of Builder as the type, but that wouldn't be particularly useful.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
-1

You're not casting superclass object to subclass there. For GMOFacts.Builder object, the calories method would be type erased to something like this:

public GMOFacts.Builder calories(int val) {
        calories = val;
        return (GMOFacts.Builder) this;
    }

And this reference points to GMOFacts.Builder object anyways, so it's a valid type cast.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • 1
    Just because the type-erased code is valid does not mean the the generic method is type-safe. – newacct Jul 06 '16 at 06:36
  • @newacct Sorry, I didn't understand. You talking about this particular method, or all generic methods in general? Can you elaborate please? – Rohit Jain Jul 06 '16 at 07:11
  • 1
    Like I can write a method ` T foo(Object x) { return (T)x; }` which would also have a valid cast when erased, but is clearly not type-safe. The fact that the erased code of the method itself does not contain a cast that can fail does not mean that the generic method is type-safe, because the generic return type causes casts to be inserted in the calling code to cast the result, and that can fail. – newacct Jul 06 '16 at 07:49
  • @newacct I do understand that, but the erased method I gave is in context of the code in the linked answer. – Rohit Jain Jul 07 '16 at 08:28
  • 1
    I am not sure what you are trying to address with this answer. If you are trying to say that there will be no `ClassCastException` in this method specifically, then yes. But the generic cast can cause a `ClassCastException` in code outside the method. – newacct Jul 07 '16 at 19:01
  • @newacct Of course I'm talking about this particular case. That is what the question is about. And yes, unsafe generic casting can result in `ClassCastException`, and that I'm aware of. Here it was specifically for the question linked. – Rohit Jain Jul 09 '16 at 08:43
  • 1
    No, I am talking about this particular case too. The cast *is* unsafe and *can* cause a `ClassCastException` under certain circumstances, just not inside the method. – newacct Jul 09 '16 at 08:44
  • @newacct Well, it would result in `ClassCastException`, if the generic subclass, type parameter doesn't has `Builder` as supertype. But then that would be different point altogether. This is the way we implement a generic builder pattern, and person do have to be careful in doing type-cast. Anyways, let's leave the discussion here only. – Rohit Jain Jul 09 '16 at 09:41
  • 1
    You mean if the subclass doesn't have `T` as a supertype? Yes, that's my point. – newacct Jul 09 '16 at 23:01