1

Given an interface such as,

public interface NumVal<C extends Number>{ 
      /* Put your most sophisticated code ever here! */
} 

is there any actual differences in runtime in using a var like

NumVal myRawVal = (...) vs

NumVal<Number> mySuperVal = (...) ?

I know in compile time the former cause some more warnings. But also after type erasure is supposed to be Number equally according to Oracle, so it is safe anyway, is it not?

Whimusical
  • 6,401
  • 11
  • 62
  • 105
  • There are already a lot of questions discussing this feature of generics. Please do a google search. – Rohit Jain Dec 15 '15 at 17:16
  • Check this one out: http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it – Rohit Jain Dec 15 '15 at 17:19
  • I searched in SO but did not find anything. I am just asking if is there any exceptionality I'm forgetting about considering them the same at runtime. Oracle says that internally this would be erased as Number. – Whimusical Dec 15 '15 at 17:20
  • @Rohit, I know what a Raw type is, I'm jjust asking if it can be safely interchanged by the type in the particular case where T extends an Interface. This is much more focused and distinctively oriented to "what a raw type is". If the question you linked was "how different is Raw from at runtime", I still could consider it is comparable – Whimusical Dec 15 '15 at 17:22
  • If the interface has no methods, as in your example, then the type parameter is obsolete and therefore it makes no difference. Show a proper example where the type parameter is used somewhere. – Adam Michalik Dec 15 '15 at 17:28
  • @Whimusical Let's take it this way: If you write a successfully compiled code using `NumVal`, and then remove `Number` type argument afterwards from everywhere, the resulting code will be same at runtime, what you've with `NumVal`. Using type parameter only helps you with compile time safety. – Rohit Jain Dec 15 '15 at 17:29
  • @Adam Sorry for not being clear with the intentions. I meant in a conceptual level, and therefore I changed a bit the question to show what is the answer I look for – Whimusical Dec 15 '15 at 17:35
  • @Whimusical Not sure how that comment is helping at all. It's difficult to answer such questions without a specific scenario. How do you expect us to rewind all the scenarios possible for such case in our minds? – Rohit Jain Dec 15 '15 at 17:41
  • Let's put it that way. If I take the whole JDK and changed any casual or any when to Raw, would it compile or wok as safely? – Whimusical Dec 15 '15 at 17:43
  • Here is a good reason not to use raw types: https://gist.github.com/jnizet/1c59d83478fc5e3fe37f – JB Nizet Dec 15 '15 at 17:44
  • @Rohit Because of the experience. Sometimes I have a lot of questions which are closer to the "can I safely assume that or or am I not taking into account something?". Is difficult in a socratic way, to ask WHAT you don't know, because you don't know that you don't know it; and because of that you need to know everything for answering with authority, and if you do, then you don't ask. I won't say I don't run into a lot of problems when asking in SO this kind of questions which are not solution oriented, but more architectonical/holistic – Whimusical Dec 15 '15 at 17:47
  • e.g: I did a mental schema of premises, "x^2=4", then x=2, am I forgetting something? Yes,x=-2 as well – Whimusical Dec 15 '15 at 17:53
  • @JB Nizet Wow, I'm realling flipping out at your code. Not sure it is still related to what I am asking because I'm amazed that `List list();` seems to be related in some way to `interface NumVal`, without the method using C class type neither being a generic method with ` List list();`... – Whimusical Dec 15 '15 at 18:09
  • @JB Nize Wait, I have an hypothesis... could it be that makeing a generic class raw, would delete the types even for those signatures with a generic not dependant on the class type? Then I could understand this as a very enlighting answer to my question. I mean if NumVal is Raw'd, then any generic signature, even if it returns an unrelated-to-T hardcoded-typed List, is assumed as List – Whimusical Dec 15 '15 at 18:14
  • 1
    Exactly. Once you use raw types, you lose generics for all the generic types in the class, even those which are not related to the generic type of the class. – JB Nizet Dec 15 '15 at 19:04
  • Crazy, why is that? This really compiles... ÷ public class NumVal { List list(){ return new ArrayList<>();}; public void test(){ (new NumVal()).list().add("hello!");} }÷ – Whimusical Dec 15 '15 at 19:14
  • That would be the best answer so far, at least we have one case where response wont be the same. Imagine instead of adding to the list, you had a method expecting an String, from the list, but now the list, with a predefined type of an String is returning an object. This code is failing, not just warningª – Whimusical Dec 15 '15 at 19:26
  • @JB Nizet Ok, I answered my own question with some testcases showing the light you shed about the different behavior – Whimusical Dec 16 '15 at 03:49

3 Answers3

2

You seem to be confused about the erasure of NumVal's declared type variable, C.

public interface NumVal<C extends Number>{ 

The declaration above introduces a new generic type named NumVal. NumVal<Integer> is an example of a parameterization of this type. NumVal is a raw type.

The erasure of C which, by the JLS definition,

The erasure of a type variable (§4.4) is the erasure of its leftmost bound.

is Number, never comes into play in these conceptual definitions.

The fact that NumVal is a raw type has consequences on how you use any reference expressions of that type. When a raw type is involved in an expression, as a method argument, as a method invocation target, etc., the other parts of the expression are also erased. Since they are erased, the compiler cannot guarantee the type safety of the expression and therefore warns you about it.

Potential issues are discussed at length in the canonical:

Community
  • 1
  • 1
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
1

If you mistakenly put a Dog in your collection of Cats, and if you have specified type parameters correctly everywhere, then the compiler will raise an error, thereby helping you correct the mistake, and avoid really unnecessary bad surprises at run time.

If you don't specify the type parameters, the compiler cannot help you, and you will likely have some very bad and very unnecessary surprises at room time. If you code perfectly then you'll be fine, you won't need help. But nobody codes perfectly, nobody.

I mean whether Dogs are or not Cats, and provided they impelment Animal, is it Zoo (raw) the same as Zoo if the interface Zoo is defined as Zoo

At run time, the types are erased. Zoo<Animal>, Zoo<T extends Animal>, Zoo<Cat>, etc, are all just Zoo. So yes, you could remove all those <...> type parameters from your code. If your code was perfectly working before, then it will continue to work perfectly. To the program at runtime, it doesn't matter whether you wrote the correct type parameters or not. But if you didn't specify the type parameters, then you might make a mistake.

The type parameters are for your protection, to catch problems as early as possible, at compile time, rather than later. So always use the most appropriate types, and never use raw types.

janos
  • 120,954
  • 29
  • 226
  • 236
  • But I am using supertype in my example, and according to oracle, a Raw type of T extends super is "rendered" as super. So does it have the same safeness at runtime? – Whimusical Dec 15 '15 at 17:28
  • I mean whether Dogs are or not Cats, and provided they impelment Animal, is it Zoo (raw) the same as Zoo if the interface Zoo is defined as Zoo? – Whimusical Dec 15 '15 at 17:30
  • 1
    @Whimusical that is a significant oversimplification that hides many other issues caused by raw types. – Louis Wasserman Dec 15 '15 at 19:05
  • @Whimusical I responded to your comment inside my answer, I hope this helps. – janos Dec 15 '15 at 19:15
  • You can write Zoo a = new Zoo() but you can't write Zoo a = new Zoo(). – user690421 Dec 16 '15 at 05:19
0

Although there are very good answers, I saw the cases for which the claim is false thanks to @JB Nizet, and as he did not ventured to adapt it as an answer, I will sum it here with this testcase basttery:

public class NumVal<C extends Number> {
    Integer       nextFibboInt  (int secondLast,int last){ return sum(secondLast,last);};
    C             nextFibboC    (C secondLast,    C last){ return sum(secondLast,last);};
    List<Integer> next2FibbosInt(int secondLast,int last){ return Arrays.asList(sum(secondLast,last), sum(sum(secondLast,last),last));};
    List<C>       next2FibbosC  (C secondLast,    C last){ return Arrays.asList(sum(secondLast,last), sum(sum(secondLast,last),last));};

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static void main (String[] args){

        Integer  singleGenericSubtype    = new NumVal<Integer>().nextFibboC(1,2);          //This is the exclusive obvious for generics
        Integer  listGenericSubtype      = new NumVal<Integer>().next2FibbosC(1,2).get(0); //This is the exclusive obvious for generics

        Number   singleGenericSuper      = new NumVal<Number> ().nextFibboC(1,2);
        Integer  singleHardcodedTyped    = new NumVal<Integer>().nextFibboInt(1,2);
        Number   listGenericSuper        = new NumVal<Number> ().next2FibbosC(1,2).get(0);
        Integer  listTyped               = new NumVal<Integer>().next2FibbosInt(1,2).get(0);

        Number   singleGenericSuperRaw   = new NumVal().nextFibboC(1,2);            // OK - These are the cases I was assuming as equivalent
        Integer  singleHardcodedTypedRaw = new NumVal().nextFibboInt(1,2);          // OK - These are the cases I was assuming as equivalent
        Number   listGenericSuperRaw     = new NumVal().next2FibbosC(1,2).get(0);   // KO - These are the cases that I did not considerate that fail the hypothesis
        Integer  listHardcodedTypedRaw   = new NumVal().next2FibbosInt(1,2).get(0); // KO - These are the cases that I did not considerate that fail the hypothesis
    }

    private <T extends Number> T sum(T o1, T o2) {
        Number sum = new BigDecimal(o1.toString()).add(new BigDecimal(o2.toString()));
        Object result = -1;
        try { result = o1.getClass().getMethod("valueOf",String.class).invoke(null, sum.toString()); } catch (Exception e){}
        return (T) result;
    }
}

  • So last 2 cases fail (at compiling), which means erased raw is not interchangable for the super <Number>. Making the class raw seems to erase ANY type of ANY method signatures of the class (Thanks @JB Nizet)
    • The first fail is due to the fact that even if C is correctly erased as Number when alone, List<C> is otherwise erased as List<Object>
    • The second fail is even stranger, and due to the fact that surprisingly my raw class is erasing as well the types of the List<Integer> hardcoded returning methods even when they are not using/related to the class C type in any way
Mr Lister
  • 45,515
  • 15
  • 108
  • 150
Whimusical
  • 6,401
  • 11
  • 62
  • 105
  • Interesting example, but I would still count this as compile time difference. If you simply add cast to the 'KO' cases, it runs the same at runtime. As you pointed out, Java compiler chooses not to add cast for generic return types of a method invocation on a raw type even though it's possible. Isn't this enough to show the intention of Java generics inventors? But really, the point is that one should not use raw type unless there is a need. Why killing yourself trying to find an edge of raw type over parameterized type when there isn't any. – user690421 Dec 16 '15 at 05:12
  • Because I wanted to understand how much different was Something> from Something [raw], as I only haf in mind the 2 OK cases. Raw'ing MyVal still works for the 2 OKs, so it is treated the same way, but I am surprised the 2 KOs fail given the same logic as the previous ones. I cannot understan why does the compiler behaves like that. – Whimusical Dec 16 '15 at 16:27
  • You still seem to be confused. The erasure of the generic type `NumVal` is `NumVal`, not `NumVal`. The raw type is `NumVal`, it's not parameterized. since you're invoking `next2FibbosC` on a reference expression of the raw type `NumVal`, the method itself is also erased. The erasure of its return type `List` is `List`, not `List`. And therefore `List#get` has a return type of `Object`, not `Number`. – Sotirios Delimanolis Dec 16 '15 at 18:01
  • But in the case of the C returning method, it returns Number when called raw, I would have imagined, a List would have been List instead of List = List – Whimusical Dec 16 '15 at 18:24
  • As I've stated in an answer, the erasure of `C` is its left-most bound, `Number`. So the erasure of the method `C nextFibboC(C secondLast,C last)` is `Number nextFibboC(Number secondLast, Number last)`. The erasure of the method `List next2FibbosC (C secondLast, C last)` is `List next2FibbosC(Number secondLast, Number last)`. – Sotirios Delimanolis Dec 16 '15 at 18:50
  • Yes, that's what I would have never guessed, if C secondLast is erased as Number, why List is erased as List() instead of List? I mean I know erasing ignores the types because its runtime, but generics also work a bit in runtime, since `String element = list.get(0);`, at the end, is casting Object to String transparently and magically once erased – Whimusical Dec 16 '15 at 19:25
  • `List` is not erased as `List`, it's erased as `List`. `List` is a raw type. `List` is a parameterized type. These are not the same. I don't see a `String element = list.get(0)` in your question or elsewhere here. I can't address it. – Sotirios Delimanolis Dec 16 '15 at 20:17
  • Yes, sorry, what I mean is that after erasure, there are still some implicit castings.. I mean a previous List after erasing, it's still s a special List as it keeps an implicit information on the type if you can still do String a = list.get(0) without casting. I mean generics don't work only as a checking system, but as a runtime assumption facilitator as I see it. That is why not everything ends up with erasure. I see it more complex- – Whimusical Dec 16 '15 at 23:44
  • If `list` is of type `List`, you definitely **cannot** do `String a = list.get(0);`. (Please respond with @myusername, so I can be notified.) – Sotirios Delimanolis Dec 17 '15 at 01:18
  • @Sotirios Delimanolis Well I meant,, at runtime, List is just List, but compiler still does some tricks after erasing, it automatically casts from Object when you execute String x = list.get(0). so not eveyrthing ends after erasion – Whimusical Dec 17 '15 at 22:23
  • No, it doesn't. That claim is [false](http://ideone.com/UtilwK). Such code would not compile if `list` is of type `List`. If it's of type `List`, the cast (which is a runtime action) is implicitly provided through the use of generics. – Sotirios Delimanolis Dec 17 '15 at 22:26
  • @Sotirios Delimanolis I'm not expressing well, since not my mother tongue sorry. What I mean is that regarding the old comments about "that is because type is erased", erasure does not justify anything per se. List is erased at compiled time, and still compiler does extra work autocasting for making String a = list.get(0); return an String instead of an Object. I mean after typechecking and erasure, generics have also effect on the runtime code, so nothing disallows the idea that just because types are erased, the possibility to have that info must be disregarded. – Whimusical Dec 17 '15 at 22:32
  • So if compiler checks types, erases, and then writes down somewhere that this list returns a String and so it must be casted in behind, why wouldn't be normal to expect that it does the same with those returning objects that seem to be converted to raws returning objects (2 fails in the example). Is not just a matter of "that's erasure". this is what I mean – Whimusical Dec 17 '15 at 22:36
  • After all, you can retrieve some of this info through reflection at runtime, so the fact that that list is expected to return a String is an info it must be sotred somewhere in the bytecode. Does it have sense? Then why not doing the same with the return types of the rawed class after checking they don't relate at all with the discarded class-type? – Whimusical Dec 17 '15 at 22:43