Consider the following code:
MyClass myClass= new MyExtendedClass();
myClass.method();
where MyExtendedClass be a subtype of MyClass. As i understood on a compile state compiler on the second string compiler checked of existence method method()in a source code of MyClass. Is it correct reasoning? Now consider
List<Integer> ints= new ArrayList<Integer>();
ints.add(2);
List<? extends Integer> lst = ints;
lst.get(0);
Where i can see the sources of List<? extends Integer>?
Now consider:
new ArrayList<Integer>().getClass().equals(
new ArrayList<String>().getClass());// return true.
So
In a run time classes ArrayList<Integer> and ArrayList<String> are equals, but on a compile state it is not true. Why? Where are the sources of ArrayList<String> and ArrayList<Integer>?
Search for type erasure. To start you can refer http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
For myClass.method(); to be legal, the MyClass class or interface must declare the method method. That is correct.
The reason that the getClass results are equal for ArrayList<Integer> and ArrayList<String> is that at runtime, type erasure erases the generic type parameters, leaving ArrayList in both cases, and of course they are both equal. It is only the compiler that can distinguish between ArrayList<Integer> and ArrayList<String>. The JVM never knew about generics, so type erasure occurs to be backwards compatible with pre-generics (pre-Java 1.5) applications.
Source code for List
Source code for ArrayList
Java does not work the way that C++ does. In C++ a "template" causes a complete generation of a new class with that referenced type in it. Java, however, does not generate a new class for every templated instantiation. Instead, the type information is carried as a setting at compile and run time. So ArrayList<Integer> and ArrayList<String> make use of the same actual class.
The type checking is in the compiler, and for simple classes this is easy.
class Gem {...}
class Ruby extends Gem {...}
class Diamond extends Gem {...}
Gem a;
Ruby b;
Diamond c;
a = b; //no problem allowed
b = a; //type problem, need to cast it!
b = c; //never allowed!
But parameterized classes are much more complicated
List<Gem> la;
List<Ruby> lb;
List<Diamond> lc;
la = lb; //not allowed because type is different even though Ruby extends Gem
List<? extends Gem> ld;
ld = la; //allowed
ld = lb; //also allowed
ld = lc; //also allowed
The wildcards are needed to allow you to have a collection variable that holds more than one kind of collection, where the differences are in the type parameter. In all cases the object class (and source code) remains the same.
Related
I can't seem to create an array of class instances that have a field of a generic.
Like so:
class Main {
public static void main(String args[]) {
Foo<String> foo = new Foo<>();
}
}
class Foo<K> {
public Bar[] arr;
Foo() {
arr = (Bar[]) new Object[10];
}
void push(int index, K value) {
arr[index].value = value;
}
class Bar {
K value;
}
}
REPL
Which gives me
Exception in thread "main" java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [LFoo$Bar; ([Ljava.lang.Object
But I do know for a fact that an array of generics, like so
T[] arr = (T[]) new Object[10];
can be created.
But I do know for a fact that an array of generics, like so
T[] arr = (T[]) new Object[10];
can be created.
Nope. This is incorrect, and that is the root of your confusion.
You can't create 'generic arrays'. Generics are a figment of the compiler's imagination. The JVM itself doesn't have a clue as to what they are. Most generics information poofs out of existence during the compilation step; the few generics that survive (in signatures), are treated as comments by the JVM: The JVM does not know what it means and doesn't care.
T[] arr = (T[]) new Object[10];
is a fancy way of telling the compiler not to complain, and to inject a few casts here and there. That is all that line does.
You've created a new object array. It's not a T[] array. The cast operator does not convert anything. It merely asserts types (is this thing indeed of that type? If yes, great, do nothing. If no, throw an exception). Given that this is by definition a runtime thing (if the compiler knows some expression is of some type, the cast obviously isn't needed), and generics fundamentally do not exist at runtime - then this cast operation truly does nothing. There's nothing to check. It cannot fail and has no bytecode. It is there just to tell the compiler that you take responsibility, as the compiler can no longer ascertain type safety for you.
What you've done is made an array of objects, and assigned it to a variable of type T[]. Local variables don't even exist in class files, they are 'compiled out', so to speak. Hence, you most definitely did not just 'make' a new 'array of T', in any sense you care to take that sentence.
So, having covered that:
Collections of any stripe (be they java.util.List or an array) are invariant. invariant is a concept in typing systems. You are presumably more used to covariant type systems: Java's basic type system is covariant. Covariant means: A subtype of a thing is just as good. In other words, this:
Object o = "hello";
is fine, because String is a subtype of Object, and is covariant - subtypes are fine. This isn't simply 'because the java spec says so' - it makes a more fundamental sense.
But in (writable) collections it just breaks down. Imagine that the 'component type' of a collection was covariant, then I could do this:
Integer[] integers = new Integer[10];
Number[] numbers = integers;
numbers[0] = 5.5;
System.out.println(integers[0]); // hmmmmm!!!
Go through those lines step by step - they explain why covariance is wrong. Hence, for the sake of collection types, java's typing system is invariant. You cannot assign an Integer[] to a Number[]. Unfortunately, 30 years ago when the initial java spec that introduced arrays was written, this wasn't thought through all that far and arrays have turned to be really really weird constructs as a consequence: Their toString, equals and hashCode implementations are well defined, but the definition is: These methods are effectively completely useless, and they can't grow or shrink.
collections do a much better job at it - you can't assign a List<Integer> list to a variable of type List<Number> either, but unlike with arrays, you can ask java for covariance and even contravariance: List<? extends Number> gives you covariance and the compiler acts accordingly - for example, you can't add anything to a List<? extends Number> because there's no way to know what you could possibly add - perhaps that variable is pointing at a List<Integer>, perhaps it is pointing at a List<Double>, and no value is both a Double and Integer simultaneously (except trivially and not useful, but for completeness: null, literally - which actually works; you can call list.add(null) if list's type is List<? extends Number> - it is the only thing you can add).
One of those weird things about arrays is that any array can be assigned / is type-compatible with Object[], even though this is wrong. It's a throwback in order to allow working with objects (what you really need is a ?[] - just like you can have a List<?>, but that syntax did not exist at the time).
Arrays, unlike generics, are not a figment of the compiler's imagination: The runtime actually knows about them, tracks them, etcetera. You can e.g. do this:
Object[] o = new String[10]; // weird, but legal java.
o[0] = 5; // compiles, but throws an ArrayStoreException at runtime.
o.getClass().getComponentType(); // returns 'String.class'
and note how generics doesn't work like this at all:
List<Object> o = new ArrayList<String>(); // does not compile.
// .. but for funsies let's force the issue:
List<String> strings = new ArrayList<String>();
List /* raw */ hack = strings;
List<Object> objects = hack; // compiles with warnings.
hack.add(5.0); // perfectly fine, compiles and runs without error.
String y = strings.get(0); // compiles perfectly fine.... but throws ClassCastException at runtime.
objects.get????? // there is no way to get 'String.class' from this thing. At all.
In other words, what you fundamentally want to do (treat an array of Dogs as an array of Animals) doesn't work - not because of java, but because of the universe: You can add parrots to an array of animal, hence why you can't treat an array of Dogs as an array of Animals. A java specific hacky thing with arrays is that you CAN treat an array of anything as an array of Object specifically (and only Object[], that is hardcoded in the spec), which is wrong and leads to all sorts of broken code, but it's in the language solely to give you an option to work with arrays as a generalized concept, because java 1.0 through 1.4 didn't have generics.
TL;DR: Do not use arrays. They are weird and mostly useless. Make List<T>s instead.
The exception caused by this line :
arr = (Bar[]) new Object[10];
To simplify the explanation i just want to replace your code with this example :
public class Main {
public static void main(String args[]) {
Bar b = (Bar) new Object();
}
}
class Bar{
}
This line Bar b = (Bar) new Object(); compile because Bar extends Object , an this inheritence relation let your code compile, but at RunTime Java find that you are trying to cast an instance of Object class ( created using new Object()) ,and that's impossible because Object is a superType of Bar .
You did the same thing just you added the [ ].
With generics , any T class must extends (directly or indirectly) Object class , T can be Object himSelf , so the code compile and can throw a ClassCastException .
In Java 7 and later, diamond can be used to infer types on normally like so without an issue:
List<String> list = new ArrayList<>();
However, it can't for anonymous inner classes like this:
List<String> st = new List<>() { //Doesn't compile
//Implementation here
}
Why is this? Logically in this scenario, I can definitely infer the type as String. Is there a logical reason for this decision where the type cannot actually be inferred on anonymous inner classes, or was it omitted for other reasons?
In the JSR-334:
Using diamond with anonymous inner classes is not supported since
doing so in general would require extensions to the class file
signature attribute to represent non-denotable types, a de facto JVM
change.
What I guess is that as everybody knows, anonymous class leads to a generation of its own class file.
I imagine that generic type doesn't exist within these files and rather replaced by the effective (static) type (thus declared by the explicit type like <String> at declaration object time).
Indeed, file corresponding to an inner class is never shared across multiple different instantiations of it, so why bother with generics into it?! :).
It would be more hardly achievable (and surely useless) for compiler to force an extension (by adding a special attribute for generics) to theses kind of class files.
google yields, after skipping posts from stackoverflow, http://mail.openjdk.java.net/pipermail/coin-dev/2011-June/003283.html
I'm guessing it's like this, usually an anonymous class is a concrete subclass of the apparent type
interface Foo<N extends Number>
{
void foo(N n);
}
Foo<Integer> foo = new Foo<Integer>(){ ... }
is implemented by
class AnonFoo_1 implements Foo<Integer>{ ... }
Foo<Integer> foo = new AnonFoo_1();
Suppose we allow diamond inference on anonymous classes, there can be complicated case like
Foo<? extends Runnable> foo = new Foo<>(){ ... }
The inference rules yield N=Number&Runnable; following the prev implementation trick, we need
class AnonFoo_2 implements Foo<Number&Runnable>{ ... }
That is currently not allowed; the type arg to super type Foo must be a "normal" type.
However, the rationale is not very strong. We can invent other implementation tricks to make it work
class AnonFoo<N extends Number&Runnable> implements Foo<N>
{
#Override public void foo(N n)
{
n.intValue();
n.run();
}
}
Foo<? extends Runnable> foo = new AnonFoo<>();
the compiler ought to be able to do the same trick.
In any case, at least the compiler should allow the majority of use cases that do not involve "undenotable types", like Foo<Integer> foo = new Foo<>(){...} It's a pity that these common/simple cases are unnecessarily forbidden too.
In short, the <> does little to infer types, it turns off the warning you would get without it.
EDIT: As #Natix points out it does some checking.
List<Integer> ints = new ArrayList<>();
List<String> copy = new ArrayList<>(ints);
produces a compilation error
Error:Error:line (42)error: incompatible types
required: List<String>
found: ArrayList<Integer>
As you can see the <> is taking the type of the argument, not inferring the type from the type of copy
In java one can cast instance of a class B into an instance of class A provided that B extends A.
Can this be done also for type-parametrized classes? For instance, for B extending A, I'd like to do the following:
List<A> l = new ArrayList<B>();
I think this should be legal, but the compiler doesn't agree with me on this point, so I tricked it with the following hack:
List<A> l = (List<A>)(List) new ArrayList<B>();
…but I think that I'll be hit by a velociraptor. Is there an elegant way of doing this?
I think this should be legal, but the compiler doesn't agree with me on this point
Generally, when humans and compilers disagree, it is safe to take the compiler's side. Instead of tricking the compiler, you should first understand why it does not allow the conversion.
Consider this: you make a List<B>, like this:
List<B> listB = new ArrayList<B>();
Everyone is happy so far. Now let's do what you think is correct:
// This does not compile, but let's pretend that it does
List<A> listA = (List<A>)listB;
At this point, we have listA that is entirely ours! Let's play with it: let's say we have another class C extending A. Can we do this?
listA.add(new C()); // Why not? The compiler should allow it!
But wait, do you see what we have done? Our listB contains a C now! This is why the compiler does not allow the conversion; this is also why your code is not safe in cases when you attempt to add anything to the list.
To work around this issue, you can use wildcards: List<? extends A>. However, this will limit your lists to read-only access. If you must allow writing (and you know that you are going to set only the right stuff) use the non-generic List instead.
List<? extends A> l = new ArrayList<B>();
You should read about generics in java here
Two ways to do it.......
- This way of handling the Collections are done, cause Array is checked during compile and as well as runtime, but Collections are checked only during Compile time.
- So there should NOT be an accidental addition of another type of object in to another type of Collection, ie Dog Object into a Cat Collection.
1.
public <T extends A> void go(ArrayList<T> aList){
}
2.
public void go(ArrayList<? extends A> aList){
}
In a non generic context, if B "is a" A, then B can be substituted for A, but A cannot be substituted for B.
With generics, List and List are both "is a" Collection<E>; that is, List is not a subclass of List, and therefore, you cannot substitute List for List.
The specific content on generics you're looking for can be found here, copied below for your convenience.
Given two concrete types A and B (for example, Number and Integer), MyClass has no relationship to MyClass, regardless of whether or not A and B are related. The common parent of MyClass and MyClass is Object.
I have a class with the following definition:
public abstract class A<T> implements Iterator<B> {}
The following call to next() will return an Object rather than a B:
A a = new SomethingThatExtendsA();
B b = a.next();
I've searched for quite awhile and haven't been able to figure out why this next() call fails to compile. Is anyone able to describe this behavior for me?
Edited original to be templated, as this seems to matter.
Edit for additional clarification: This is a compile-time issue, not a runtime issue. The implementation of SomethingThatExtendsA(); should be irrelevant in this case at compile-time.
So we have this code:
public abstract class A<T> implements Iterator<B> {}
[...]
A a = new SomethingThatExtendsA();
a.next();
A is a generic type, but you have defined a with a raw type. Ignore the right hand side of the =, we are only interested in the static type:
A/*<Something>*/ a = ...;
The compiler should give you warnings here. (At least relatively recent versions of javac will do - rawtypes warning in Oracle javac.) Take notice of your compiler's warnings. (Wasn't it nice when javac didn't give warnings?)
So now we are in a situation that a has a type that is both raw and is a Iterator<B>. This is a really confusing situation with just mind-blowingly difficult implications. We shouldn't even be doing this - we should be avoiding mixing generic and raw types. So the Java Language Specification takes the simple way out and discards the partial generic type information.
So, don't mix raw and generic types. Just use all generics and you should be fine.
public abstract class A implements Iterator<B> {}
class B {}
class SomethingThatExtendsA extends A {
// implement A methods
}
A a = new SomethingThatExtendsA();
a.next();
This code is pretty correct and returns B object. Probably you missed something in SomethingThatExtendsA class.
Test it by calling:
B b1 = new Object(); // compilation error
B b2 = a.next(); // all is OK
UPDATE:
Change signature in SomethingThatExtendsA from
public Object next() { } to public B next() {}
The SomethingThatExtendsA class is a red herring here. You get the same problem with
List<String> list = new ArrayList<String>();
list.add("foo");
Iterator iterator = list.iterator(); // you should get a warning on this line...
String foo = iterator.next(); // ... and a compile error on this line
The issue is that raw types are a horrible ugly hack for backwards compatibility only which cause all the generics around them to stop working. Specifically, Iterator is not the same type as Iterator<String>, Iterator<Object>, or even Iterator<?>. Instead, Iterator means "the Iterator class I would have got if I had compiled with a pre-generics version of Java". Since the pre-generics version of Iterator always returned Object, then that's what you get. And you can't assign a value of type Object to a more specific type (like String, or your type B) without a cast.
The Java Language Specification says this:
The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of genericity into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types.
Tiger class is extends from Animal Class.
When I declare: List<Animal> tiger = new ArrayList<Tiger>();. I will error at compile-time.
But, I think this line is true for polymorphism. Who can explain for me, please.
you cannot do
List<Animal> tiger = new ArrayList<Tiger>();
that in java. Generic type on left have to be exacly equal (or may not have to be equal, if wild cards are in game - ? extends T or ? super T) to generic type on right.
If it was possible then it would be impossible to add new Lion to list declared as list of Animals - that would make no sense.
What you can do is:
List<Animal> tigers = new ArrayList<Animal>();
tigers.add(new Tiger());
(all family of Animals, including Tigers)
or:
List<? extends Animal> tigers = new ArrayList<Tiger>();
tigers.add(new Tiger()); // Adding is immpossible now - list can be read only now!
(only subclasses of Animal) - list can be read only now!
A List<Animal> would allow you to add a cute little puppy. Which the tigers in the ArrayList<Tiger> would then eat.
Polymorphically speaking, you would have
List<Tiger> tigers = new ArrayList<Tiger>();
Which would allow you to replace use any implementation of List<Tiger> if you so desired, relying upon and using the functionality as defined by the interface. What you are trying to do isn't polymorhpism, it is simply an unsafe conversion (particularly for the aforementioned puppy) and is not going to work for reasons illustrated above.
The reasons for this are based on how Java implements generics. The best way I have found to explain it is by using arrays first.
An Arrays Example
With arrays you can do this:
Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
But, what would happen if you try to do this?
Number[0] = 3.14; //attempt of heap pollution
This last line would compile just fine, but if you run this code, you could get an ArrayStoreException.
This means that you can fool the compiler, but you cannot fool the runtime type system. And this is so because arrays are what we call reifiable types. This means that at runtime Java knows that this array was actually instantiated as an array of integers which simply happens to be accessed through a reference of type Number[].
So, as you can see, one thing is the real type of the object, an another thing is the type of the reference that you use to access it, right?
The Problem with Java Generics
Now, the problem with Java generic types is that the type information is discarded by the compiler and it is not available at run time. This process is called type erasure. There are good reason for implementing generics like this in Java, but that's a long story, and it has to do with binary compatibility with pre-existing code.
But the important point here is that since, at runtime there is no type information, there is no way to ensure that we are no committing heap pollution.
For instance,
List<Integer> myInts = new ArrayList<Integer>();
myInts.add(1);
myInts.add(2);
List<Number> myNums = myInts;
myNums.add(3.14); //heap polution
If the Java compiler does not stop you from doing this at compile time, the runtime type system cannot stop you either, because there is no way, at runtime, to determine that this list was supposed to be a list of integers only. The Java runtime would let you put whatever you want into this list, when it should only contain integers, because when it was created, it was declared as a list of integers.
As such, the designers of Java made sure that you cannot fool the compiler. If you cannot fool the compiler (as we can do with arrays) you cannot fool the runtime type system either.
As such, we say that generic types are non-reifiable.
Evidently, this would hamper pollymorphism as well pointed out. The solution is to learn to use two powerful features of Java generics known as covariance and contravariance.
Covariance
With covariance you can read items from a structure, but you cannot write anything into it. All these are valid declarations.
List<? extends Number> myNums = new ArrayList<Integer>();
List<? extends Number> myNums = new ArrayList<Float>()
List<? extends Number> myNums = new ArrayList<Double>()
And you can read from myNums:
Number n = myNums.get(0);
Because you can be sure that whatever the actual list contains, it can be upcasted to a Number (after all anything that extends Number is a Number, right?)
However, you are not allowed to put anything into a covariant structure.
myNumst.add(45L);
This would not be allowed, because Java cannot guarantee what is the actual type of the real object. It can be anything that extends Number, but the compiler cannot be sure. So you can read, but not write.
Contravariance
With contravariance you can do the opposite. You can put things into a generic structure, but you cannot read out from it.
List<Object> myObjs = new List<Object();
myObjs.add("Luke");
myObjs.add("Obi-wan");
List<? super Number> myNums = myObjs;
myNums.add(10);
myNums.add(3.14);
In this case, the actual nature of the object is a List of Objects, and through contravariance, you can put Numbers into it, basically because numbers have Object as the common ancestor. As such, all Numbers are objects, and therefore this is valid.
However, you cannot safely read anything from this contravariant structure assuming that you will get a number.
Number myNum = myNums.get(0); //compiler-error
As you can see, if the compiler allowed you to write this line, you would get a ClassCastException at runtime.
Get/Put Principle
As such, use covariance when you only intend to take generic values out of a structure, use contravariance when you only intend to put generic values into a structure and use the exact generic type when you intend to do both.
The best example I have is the following that copies any kind of numbers from one list into another list.
public static void copy(List<? extends Number> source, List<? super Number> destiny) {
for(Number number : source) {
destiny.add(number);
}
}
Thanks to the powers of covariance and contravariance this works for a case like this:
List<Integer> myInts = asList(1,2,3,4);
List<Integer> myDoubles = asList(3.14, 6.28);
List<Object> myObjs = new ArrayList<Object>();
copy(myInts, myObjs);
copy(myDoubles, myObjs);
I agree it's confusing. Here's what could go wrong if that type of statement were allowed:
List<Tiger> tigers = new ArrayList<Tiger>(); // This is allowed.
List<Animal> animals = tigers; // This isn't allowed.
tigers.add(new Lion()); // This puts a Lion in tigers!
Oh. Yes, you true. your code is right in Polymorphism thinking. But, look at my code that I use for a long time when I just a novie.
And you will see why you should thank to Collection:
class Animal{
}
class Tiger extends Animal{
}
public class Test {
public static void main (String[] args){
List<Animal> animal = new ArrayList<Animal>(); //obvious
List<Tiger> tiger = new ArrayList<Tiger>(); //obvious
List<Animal> tigerList = new ArrayList<Tiger>(); //error at COMPILE-TIME
Animal[] tigerArray = new Tiger[2]; //like above but no error but....
Animal tmpAnimal = new Animal();
/*
* will meet RUN-TIME error at below line when use Array
* but Collections can prevent this before at COMPILE-TIME
*/
tigerArray[0] = tmpAnimal; //Oh NOOOO. RUN-TIME EXCEPTION
/*
* Below examples WRONG for both Collection and Array
* Because here is Polymorphism problem. I just want to make more clearer
*/
List<Tiger> animalList = new ArrayList<Animal>();
Tiger[] animalArray = new Animal[2];
}
}
As you see my above code, Collections is so "intelligent" when prevent you use List<Animal> tigerList = new ArrayList<Tiger>();
You should imagine if someone use: tigerList.add(a Lion, a Cat,......); --->ERROR.
So, to Sumarize, here is the different:
ARRAY: check at RUN-TIME. You will feel more comfortable but DANGEROUS
COLLECTIONS: check at COMPILE-TIME. You will feel angry because it
notice error. But, you will prevent errors when Running. !!!!
Maybe below post is over of your question. But I suggest you use WildCard like:
List<? extends Animal> tigerList = new ArrayList<Tiger>();
Yes. You might see the idea behind this line. But, the MOST INTERESTING THING is: it will prevent you change the List. in this case, add method.
For example:
tigerList.add(TIGER); ERROR
yes. It will prevent you add a tiger, too :)