Java generics with extends keyword in method [duplicate] - java

This question already has answers here:
Generics : List<? extends Animal> is same as List<Animal>?
(3 answers)
Closed 5 months ago.
I am going through the Java generics. I came across the below line.
public <T extends Building> List<T> fromArrayToList(T[] t) {
...
}
At compile time this will get converted like below,
public List<Building> fromArrayToList(Building[] t) {
...
}
With this understanding, I could rather use like below, same as compiled version in my code.
public List<Building> fromArrayToList(Building[] t) {
...
}
What is the reason behind using List<? extends Building> instead of List<Building> ?

Generics are almost entirely compiler-checked documentation. In the sense that the runtime (java.exe) has no idea what they are - most of the generics doesn't survive compilation in the first place ('erasure').
So, that it gets 'converted' to something else at compile time is irrelevant. Generics are entirely about having the compiler check your work. It does nothing at runtime.
So what does it do? It links things. It tells the compiler: There is some type, we don't know what it is, but in these places (more than one place, it's not useful to link one thing to nothing else), it's the same type, whatever it is. Any 'usage' of this situation gets to redefine whatever type it is, so long as they maintain the 'linkage'.
For example, trivially, imagine this method:
public Object print(Object o) {
System.out.println(o);
return o;
}
A simple-ish method that you can use to on-the-fly print things while you use them. For example, you may want to lowercase incoming usernames to check them against a (case insensitive) table of usernames like so: users.get(username.toLowerCase()), but perhaps you want to see the original, as-entered, username on the fly, so you could instead do:
users.get(print(username).toLowerCase());
but that would not work! The return type of the print method is Object, and Object does not have a .toLowerCase() method.
Yes, you have eyes. So have I: We can clearly see the object returned by print is clearly a string so it should work, but it does not. This is not a javac oversight - this is intentional behaviour: You are free to change the body of a method later. There is a difference between what you declare to be the truth about your API and what so happens to occur this particular version. public Object print(Object o) declares that you will today and forever (or at least, until you say your API is no longer backwards compatible), take 1 argument, that can be any object, and this method returns something. What? Dunno, but, it's at least an Object (not particularly useful, that).
Today it returns what you passed to it. But tomorrow? Who knows.
So let's fix this and make it work again. Add generics. We are going to link the type of the parameter with the return type:
public <T> T print(T object) {
System.out.println(object);
return object;
}
And now maps.get(print(username).toLowerCase()) does work! Great!
The reason it works is simply because you linked the types: You've told the compiler: Hey, this print method? Each time it is invoked, there is some type. No restrictions on it (well, it has to be Object or some subtype thereof, so not a primitive, other than that - anything goes). Both the return type and the param type are that type. Whatever type is most convenient at the time. So, given that rule - the compiler picks String to be that type and now the code works, great.
This is entirely the compiler doing it. If you decompile the bytecode, you'll notice the print method is just public Object print(Object object), and instead it is the caller that changed a bit - decompiling the users.get(print(username).toLowerCase()) line you notice that the compiler has injected a cast of all things, even though the java line doesn't contain a cast. That's because the compiler realised it was safe to do this, because of the generics.
The same applies to your code. You write it like the first snippet (with the T), so that this works:
class Highrise extends Building {}
Highrise[] highrises = ....;
List<Highrise> asList = fromArrayToList(highrises);
Had you gone with the 3rd snippet in your code, that would not work. In fact, the third snippet is broken. After all, I could call this:
List<Building> highrises = ...;
highrises.add(new LogCabin());
After all, a LogCabin is a building. Given that I can trivially write:
Building b = new LogCabin();
It would be bizarre indeed if highrises.add(new LogCabin()) would fail if highrises is a List<Building>. But.. you just added a logcabin to a list of highrises, you broke it.
A second thing to realize with generics is variance. Given a list of X, that cannot just be treated as a list of Y, where Y is a supertype. This makes total sense:
Building b = new LogCabin(); // fine
but this does not work:
List<Building> b = new ArrayList<LogCabin>(); // fails at compile time
why? Well, because you can invoke b.add(new Highrise()) and now there's a highrise in your list of logcabins:
List<LogCabin> logCabins = new ArrayList<LogCabin>();
List<Building> buildings = logCabins;
buildings.add(new Highrise());
LogCabin lc = logCabins.get(0);
The above code proves why invariance is neccessary. ? extends lets you opt into covariance, but, to counter the above problem, you can't call .add on a List<? extends> anything. Even just List<?> (which is short for List<? extends Object>. (Well, to get pedantic, you can call .add(null) - a literal null literal, because a null literal is all reference types at once, but that is quite useless, generally).
By disabling 'add', it now no longer matters. The problem with letting you assign a list of logcabins to a list of buildings is that this means one could add non-logcabin building to the list, but if add doesn't work at all, it no longer matters. Hence:
List<LogCabin> logCabins = new ArrayList<LogCabin>(); // fine
List<? extends Building> buildings = logCabin; // compiles fine
buildings.add(new Highrise()); // compiler error
Now you know why extends is useful.
Generics is best understood by keeping two things in mind:
Type variables are named that way for a good reason: There is a TYPE, but you do not know what it is. Just like int x; says "there is an integer, no idea if it's 0, 1, 5, 1238123, who knows? It has a specific file for any specific invocation, but it could be different every next invocation" - <T> says there is some type, who knows what it is.
They LINK things. If they don't link things, it's useless or a language hack. A type var must be used in at least 2 places.
Example of 2 place usage:
class ArrayList<T> { // first use
void add(T elem) { ... } // also here
T get(int idx) { ... } // and here
void addAll(List<? extends T> elems) {} // and here
}
at least 4 places where T is used, that's fine. Linkage achieved.

Related

Bad return type in lambda expression when using Java's Optional.or() with subclasses

I am trying to use Optional.or to get an object of subclass A or, if empty, an object of subclass B:
interface Node {}
class InnerNode implements Node {}
class LeafNode implements Node {}
Optional<InnerNode> createInnerNode() {}
Optional<LeafNode> createLeafNode() {}
Optional<Node> node = createInnerNode().or(() -> createLeafNode());
When doing this, I get the following compiler error:
Bad return type in lambda expression: Optional<LeafNode> cannot be converted to Optional<? extends InnerNode>
If I instead use wildcards to explicitly tell the compiler that the Optionals contain an object extending from Node:
Optional<? extends Node> optionalInner = createInnerNode();
Optional<? extends Node> optionalLeaf = createLeafNode();
Optional<Node> node = optionalInner.or(() -> optionalLeaf);
I get the following compiler error:
Bad return type in lambda expression: Optional<capture of ? extends Node> cannot be converted to Optional<? extends capture of ? extends Node>
The only way I found to make this work, is using an Optional.empty in front:
Optional<Node> node = Optional.<Node>empty() // requires generic argument
.or(() -> createInnerNode())
.or(() -> createLeafNode());
But for me, this is confusing to read. Is it somehow possible to instruct the compiler to allow the statement optionalInner.or(() -> optionalLeaf)? Or are there other alternatives to make this work?
The workaround you provided is a pretty simple answer, but you can convert an Optional<InnerNode> to an Optional<Node> trivially with map if you want to avoid a placeholder Optional.
createInnerNode.<Node>map(Function.identity()).or(() -> createLeafNode());
In general, this is also how you conceptually turn a List<Dog> to a List<Animal> or any generic collection to a different generic collection in java as well. It's somewhat annoying, but wildcards don't cut it because you want both read/write on the transformed collection.
The signature of or should tell you why your workaround works
Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)
notice how it's already using the ? extends T in the supplier.
Since the first empty Optional's T is Node, the or function can accept a supplier that returns
Optional<? extends Node>
and since both the innernode and leafnode are an Optional<? extends Node>, this works.
So the problem is that you need an Optional<Node> at some point.
I don't think you can avoid doing something since an Optional<InnerNode> never type matches Optional<Node> for reasons listed in other answers (and other questions on stack overflow).
Reading the other answer and trying
Optional<? extends Node> node = createInnerNode();
means that the T is a
? extends Node
which makes or seem to want doubly nested captures? I can't stop getting
Bad return type in lambda expression: Optional<capture of ? extends Node> cannot be converted to Optional<? extends capture of ? extends Node>
when trying basically any cast with the following code
Optional<? extends Node> node = Node.createInnerNode();
node.or(() -> (Optional<? extends Node>) null);
This must be a confusing error. Let me first explain the underlying problem so you actually understand why javac is complaining, and then I'll get to how to fix it.
The underlying problem is lack of use-site variance.
The error you get just doesn't make sense for Optional, but it makes perfect sense when we use a type that produces and consumes typed data (unlike Optional which, as an instance, only produces data, e.g. when you call .get() - there is no .set() method on Optional; it's immutable).
So let's look at lists. Imagine you could do this:
Integer i = 5;
Number n = i; // This is legal java.
List<Integer> ints = new ArrayList<Integer>();
List<Number> numbers = ints; // So this should be too... right?
That 4th line is not legal. Because generics are by default invariant (Neither supertypes nor subtypes will do; in an invariant typing system, if a Number is required, then only a Number will do) - and that is correct. After all, if the above code WAS legal, then everything breaks:
Double d = 5.0;
numbers.add(d); // Fine - must be. doubles are numbers!
Integer i = ints.get(0); // um.. wait. uhoh.
Because numbers and ints are both just referencing the exact same list, if I add a double to numbers, that means I also added one to ints, and - boom. It's broken.
Now you know why generics are invariant.
This also explains a few further compiler errors:
List<Integer> ints = new ArrayList<Integer>();
List<Number> numbers = ints; // nope, won't compile.
List<? extends Number> numbers = ints; // but this will!
numbers.add(anything-except-literally-null); // this wont!
With ? extends you are telling java that you want covariance (covariance = a subtype of X is just as good as X itself. Java's non-generics typing is covariant). However, covariance in generics automatically means that ALL ways to 'send' typed data to that thing are disabled. You CANNOT add anything (except literally .add(null) as null is all types) to a List<?> or a List<? extends>. This makes sense if you think about it:
A List<? extends Number> could be a List<Integer> or a List<Double>. So how can you add anything to this list? There is nothing that is both a Double and an Integer (well, except the literal null), so nothing you could possibly pass to add is neccessarily 'safe'. Hence why the compiler always tells you its a typing violation.
This also means trivially that this won't work either:
List<? extends Number> numbers = ...;
List<Number> numbers2 = numbers; // nope, this won't compile
Back to your code
Now, for optional, none of this seems relevant. Optional can be completely covariant in its type argument because Optional does not 'consume' data - none of its methods take in an 'E'. But the compiler isn't some sort of megabrain that susses this out and lets you 'break' the variance rules of typeargs. Not so - Optional needs to stick to the same rules list does. So, if an instance of List<? extends Number> cannot be assigned to a variable of type List<Number>, then the same applies to Optional:
An instance of type Optional<? extends Node> cannot be assigned to a variable of type Optional<Node>
And that explains your errors (for both of the attempts to solve the problem stated in your question).
Even though for optional specifically these errors don't make much sense. Now you know why you are getting them.
The best fix is to not use optional here; this isn't really what its meant for and it's not very java-like (see below). But the distant second best option is to fix this stuff. The root fix is that all places that take in an Optional should ALWAYS be stated as ? extends. So, you're trying to assign it to a field of type Optional<Node>, but that field is 'wrong'. That field's type should be Optional<? extends NOde>. Then that fixes all problems:
Optional<? extends Node> node = createInnerNode().or(() -> createLeafNode());
Works fine (that's your first snippet, but with the 'type' of the target fixed).
Ah. But, I can't change the API
Well, then the API is just wrong. It happens - sometimes you need to interact with broken code. But make no mistake, that is broken code. You do the same thing you do with any other broken code: Encapsulate and work around.
You don't want to interact with broken API. So make a wrapper type or helper methods that 'paper over' the error. And in that code (the 'bridging' code that works around the buggy code you cannot change), you accept that you need to write wonky code that generates warnings.
You can 'fix things' as follows:
public Optional<Node> fixIt(Optional<? extends Node> node) {
Optional raw = node;
return raw;
}
This works because generics in the end is just a figment of javac's imagination - the JVM doesn't know what generics are at all. So we just need to 'fake out' javac to stop refusing to compile the code. The above WILL generate warnings and any errors in such hackery result in getting ClassCastExceptions on lines with zero casts on them - quite confusing. But, works fine if you know what you're doing. You can eliminate the warnings with a #SuppressWarnings annotation.
Or.. just don't use optional
Java has null baked into many many places. Not just in its core model (fields that aren't initialized begin with null values. Arrays of non-primitives are initialized with null values), but also in a great many of its most crucial libraries. The most obvious and simple example is java.util.Map: It has a get() method that returns a V. Not an Optional<V>. And in a world where Optional is the right answer, get() should of course return Optional<V>. But it does not, and it never will - java doesn't break backwards compatibility that drastically.
Thus, the hypothetical world of 'optional everywhere!' sucks - 30 years of built up code and history needs to be tossed in the bin first and the community is rather unlikely to do that. If you dislike null handling in java (and there are plenty of fine reasons to do so!), then a solution is required that lets existing code backwards-compatibly adjust, and Optional cannot deliver. (nullity annotations probably can, though! Much better; or just write better APIs. Map has .getOrDefault which can be used to ensure your map query calls don't ever return null in the first place. Once you start writing your code to never produce null values except in cases where you truly WANT any attempt to deref to throw (i.e. very rarely), then null ceases to be a problem. These more modern addons to the various java core library types make it much easier to write such code. And adding methods to interfaces is backwards compatible, so, as java.util.Map shows, existing stuff can backwards-compatibly add these).
The best thing to do with Optional is to use it solely for why it was introduced in the first place: As a return value for stream API terminals. Something like intList.stream().max() returns an OptionalInt because what is the maximum of an empty list? Its fine enough for that. If you write your own collectors or stream terminating operations, use Optional. If that's not what you are doing, don't use it.

Why should I have to specify <?> when using a generic in Java?

I'm quite new in Java, although I have much experience in C++ and other languages. So templates/generics are not something I don't know.
There's something that bothers me though, it is this <?> that I was told I should use everytime I use a generic instance of something when I don't know in advance of which specific type it will be:
Like:
List< MyGeneric > foo; // bad
List< MyGeneric<?> > bar; // good
IntelliJ doesn't barf on me when using the first expression, and I don't understand why it should. My coworkers have expressed that the 2nd expression was much better, but couldn't tell me exactly why.
I mean, what exactly is the difference between these two, apart from the second being explicit about the fact that it is a generic that we manipulate ?
The compiler certainly knows that it is a generic at compile time, so my guess is that the second expression is only better because it tells the programmer that he is manipulating a generic.
Am I right?
Edit: for clarification, I ovbiously use the most restrictive type, like List<MyGeneric<Double>>, whenever I know in advance what I am going to store in there. My question is for when I store unknown types of generics.
Every time? It's not applicable always, and it doesn't always make sense.
Let's describe what that actually is: <?> is an unbound wildcard, which immediately implies two things:
MyGeneric is a generic class, but
You do not know what type it's holding (and it likely doesn't matter).
It is preferable to the first expression in that the first expression always guarantees that you'll be working with a raw type, and you really don't want to use raw types. However, it is a gross overgeneralization to assume that using an unbound wildcard every time would be ideal.
If you actually know or care the type, or know or care about its bounds, use that instead.
Let's give an example of why it's bad to use the first. Assuming MyGeneric is defined like this:
class MyGeneric<T> {
private final T instance;
MyGeneric(T instance) { this.instance = instance; }
T get() { return instance; }
}
The following code would compile and run, but fail at runtime with a ClassCastException:
List<MyGeneric> list = new ArrayList<>();
list.add(new MyGeneric<>("Hello"));
for (MyGeneric instance : list) {
Integer value = (Integer) instance.get(); // Compiles, but fails at runtime.
}
This compiles because you're using raw types: the compiler doesn't know that instance.get() can't return an Integer; it would merely warn you that it might be unsafe.
On the other hand, the following code would not even compile:
List<MyGeneric<String>> list = new ArrayList<>();
list.add(new MyGeneric<>("Hello"));
for (MyGeneric<String> instance : list) {
Integer value = (Integer) instance.get(); // Won't compile, incompatible types.
}
The difference is that a raw type ignores the fact that the class is a generic, while the wildcard <?> specifies that the class is a generic but the type argument is unknown.
Raw means that you lose all compiler type-checking. Wildcard keeps type-checking intact.
Example:
public class MyGeneric<T> {
private T val;
public T get() {
return this.val;
}
public void set(T val) {
this.val = val;
}
}
MyGeneric a = new MyGeneric<Integer>();
a.set("Foo"); // accepted
Setting the value for a to a String when it was declared to be an Integer is accepted by the compiler, because a was defined raw, which means that the compiler is ignoring the fact that the class is a generic. When val is later used as an Integer, the program will crash. It's a bomb waiting to go off.
MyGeneric<?> b = new MyGeneric<Integer>();
b.set("Bar"); // compile error
Trying to set the value for b will not compile:
The method set(capture#1-of ?) in the type MyGeneric<capture#1-of ?> is not applicable for the arguments (String)
Here the compiler knows that the class is a generic and will not allow setting the value to anything (even an Integer), because it doesn't know what type would be allowed (wildcard = unknown, remember?). The compiler safeguards here, as it should.
List<?> means a list typed to an unknown type. This could be a List<A>, a List<B>, a List<String> etc.
Since the you do not know what type the List is typed to, you can only read from the collection, and you can only treat the objects read as being Object instances. Here is an example:
public void processElements(List<?> elements) {
for(Object o : elements){
System.out.println(o);
}
}
The processElements() method can now be called with any generic List as parameter. For instance a List<A>, a List<B>, List<C>, a List<String> etc. Here is a valid example:
List<A> listA = new ArrayList<A>();
processElements(listA);
Following tutorials will further help you to understand it:
https://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
http://tutorials.jenkov.com/java-generics/wildcards.html

Java Object return type vs. Generic Methods

I saw several questions about generic return type, but none answers my question.
If there is no bound for any of the arguments, such as the following method in JayWay :
public static <T> T read(String json, String jsonPath, Filter... filters) {
return new JsonReader().parse(json).read(jsonPath, filters);
}
What is the point of using this as generic ?
I told the guys from my team that this method should be used as :
JsonPath.<Boolean>read(currentRule, "$.logged")
instead of:
(boolean) JsonPath.read(currentRule, "$.logged")
But I really can't tell the difference...
Generics work by the compiler inserting invisible casts into your code.
For example, before generics were added to the language you'd have to do this.
List list = new ArrayList();
list.add("Foo");
list.add("Bar");
String str0 = (String) list.get(0);
String str1 = (String) list.get(1);
This was very annoying. Because get() returned Object, you had to cast every single time you wanted to get a String from the List.
Nowadays, List is generic, and get() returns T, so you can just do this.
List<String> list = new ArrayList<>();
list.add("Foo");
list.add("Bar");
String str0 = list.get(0);
String str1 = list.get(1);
What is happening here is that the compiler turns the new version into the old version by adding the casts for you, but they're still there.
However, the entire point of generics is that these compiler generated casts are guaranteed to be safe - i.e. they can't possibly throw a ClassCastException at runtime.
In my opinion, if you use generics to hide casts that are not guaranteed to be safe, just because they're annoying, it is an abuse of the feature.
Whether it's a generic method and you do
Boolean a = JsonPath.<Boolean>read(currentRule, "$.logged");
or it returns Object and you do
Boolean a = (Boolean) JsonPath.read(currentRule, "$.logged");
both versions could throw a ClassCastException at runtime, so I think it's better if you are forced to cast so that at least you are aware that you're doing something that could fail.
I consider it bad practice for the return type of a generic method to involve the type parameter T if the method parameters do not, unless the returned object cannot be used in a way that compromises type safety. For example,
public static <T> List<T> emptyList()
in Collections is ok (the list is empty so it can't contain an element of the wrong type).
In your case, I think the read method should not be generic and should just return Object.
The main reason that I would stay away from
JsonPath.<Boolean>read(currentRule, "$.logged")
is that it is internally performing an unchecked cast, and hiding this fact. For instance, you could invoke this method at the same place:
JsonPath.<String>read(currentRule, "$.logged")
and there is no way that you'd know there might be a problem there until it actually happens at runtime - it still compiles, and you don't even get a warning.
There is no getting away from the unchecked cast - I'd just rather have it right there in front of me in the code, so I know there is a potential danger; this allows me to take reasonable steps to mitigate the issue.
#SuppressWarnings("unchecked") // I know something might go wrong here!
boolean value = (boolean) JsonPath.read(currentRule, "$.logged")
Having a type-parameter that has never been set (when calling JsonPath.read(currentRule, "$.logged")), actually makes the compiler completely ignore all the generic information within the method and replace all the type-parameter with:
Object, if the type-parameter doesn't have an upper-bound. (like in your case)
U, if the type-parameter is bounded like <T extends U>. For example, if you have a <T extends Number> as a type-parameter and ignore it by calling JsonPath.read(...), then the compiler will replace the type-parameter with Number.
In the case with the cast ((boolean) JsonPath.read(...)), the type-parameter is replaced with Object. Then, this type is unsafely transformated to boolean, by first returning a Boolean (probably), and then auto-unboxing this wrapper to boolean. This is not safe, at all. Actually, every cast is not safe - pretty much you tell the compiler: "I know what this type will be at Runtime, so please believe me, and let me cast it to something else that's compatible with it.". Your humble servant, the compiler, allows that, but that's not safe, if you're wrong. :)
There's another thing with your method, also. The type-parameter is never used within the method body or parameters - this makes it pretty redundant. Since by doing a cast to boolean you insist that you know the return type of new JsonReader().parse(json).read(jsonPath, filters);, then you should just make the return type boolean (or Boolean):
public static Boolean read(String json, String jsonPath, Filter... filters) {
return new JsonReader().parse(json).read(jsonPath, filters);
}
There is nothing functionally different between the two. The byte-code will probably be identical.
The core difference is that one uses a cast while the other uses generics.
I would generally try to avoid casting if there is any alternative mechanism and as the generic form is a perfectly effective alternative I would go for that.
// The right way.
JsonPath.<Boolean>read(currentRule, "$.logged");

Java Generics: Instantiating arrays with parameterized types: illegal?

I must be confused here.
I read everywhere that in generics arrays of parametrized types are illegal.
Example from AngelikaLanger:
static void test() {
Pair<Integer,Integer>[] intPairArr = new Pair<Integer,Integer>[10] ; // error
addElements(intPairArr);
Pair<Integer,Integer> pair = intPairArr[1];
Integer i = pair.getFirst();
pair.setSecond(i);
}
Quote from Langer (but everywhere else I read it says the same thing):
The compiler prohibits creation of arrays whose component type is a
concrete parameterized type, like Pair in our
example. We discussed in the preceding entry why is it reasonable
that the compiler qualifies a Pair[] as illegal.
So far ok.
But in my code here:
private MyEntry<E> [] elements = (MyEntry<E>[])new Object[capacity];
I do exactly that, it compiles fine (I use eclipse) but get a class cast exception error (Object can not be cast to MyEntry):
My question is, why does this line compiles in the first place?
I thought that this instantiation is disallowed by the compiler.
What I am doing wrong/differerent here?
UPDATE:
On the same page, why am I able to succesfully do:
List<E> elements[] = (List<E>[])new LinkedList[capacity];
and have no runtime exceptions?
UPDATE:
Everywhere I have read (mentioned Langer since she's quoted often) it says that this declaration (arrays of parametrized types) is disallowed by compiler.
I can understand what happens after that.
I can't understand why the compiler doesn't report an error.
I am not judging, I am saying everywhere I read, it says this does not compile.
Am I missreading something?
UPDATE:
I saw some comments related to the missing parameter in the new part.
This also has no issue:
List<Entry<KeyType, ValueType>> table[] = (List<Entry<KeyType, ValueType>>[])new LinkedList[capacity];
In your first example, there's no problem with the instantiation - here's exactly what you're creating:
new Object[capacity]
Perfectly legal. You do however get a runtime exception when you attempt to cast, because an array of Object is not an array of MyEntry<E>. You might have a point that the cast or declaration could be rejected by the compiler, if these generically-parameterised arrays can't exist, though this depends what order erasure kicks in. In any case, the instantiation itself is fine.
In the second example, you're creating a non-generic array of LinkedList. You then assign it to a genericised reference, which at runtime will have been erased to just a List[]. This works fine (because rightly or wrongly, arrays are covariant).
I'm not sure why you were expecting a runtime exception; it's not much different to calling, say
List<E> = new LinkedList();
You would get some unchecked warnings, but nothing that would stop the code compiling or running.
You have completely misunderstood whatever you have read. There is absolutely nothing wrong with having the type that is an array of a parameterized type: MyEntry<E>[] or HashMap<String,Integer>[][] or whatever. You can have variables of such types all you want, and use them anywhere a type can be used.
However, with array creation, you cannot do something like new MyEntry<E>[...]. It is not allowed by the language (for type safety reasons we will not go into here), so it is a compile error.
The best solution is either new MyEntry[] (array of raw type) or new MyEntry<?>[] (array of wildcard type); either one is allowed by the language. Both of them will require you to do an explicit cast back to MyEntry<E>[].
Since you ask about your code examples, your first example is syntactically correct (there is nothing wrong with new Object[...], and it is syntactically okay to cast to MyEntry<E>[]), so there is no compile error. However, the runtime check of the cast fails at runtime, because the object's actual type Object[] is not a subtype of MyEntry[].
The second code example is also syntactically correct, and plus the runtime check of the cast succeeds (LinkedList[] is a subtype of List[]).
Because LinkedList is an instance of List.
But Object is NOT an instance of MyEntry.
Also compiler don't check can one object be cast to another or not. Because it is runtime operation.
You should use:
private MyEntry<E> [] elements = new MyEntry [capacity];
Or:
class SomeOtherEntry extends MyEntry {}
private MyEntry<E> [] elements = new SomeOtherEntry [capacity];
But not:
class SomeOtherEntry extends MyEntry {}
private SomeOtherEntry <E> [] elements = new MyEntry [capacity];
UPDATE:
List<Entry<KeyType, ValueType>> [] table = (List<Entry<KeyType,ValueType>> []) new Linked[capacity];
Built-in Java List classes actually use a work-around whenever you use the
<T> T[] toArray(T[] a) method. If we take a closer look at the code, if you supply an array that's smaller than required, the method actually creates a new array of the type parameter.
Let's see the code: https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/java/util/LinkedList.java#L1085
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
...
}

Why is List<Number> not a sub-type of List<Object>?

public void wahey(List<Object> list) {}
wahey(new LinkedList<Number>());
The call to the method will not type-check. I can't even cast the parameter as follows:
wahey((List<Object>) new LinkedList<Number>());
From my research, I have gathered that the reason for not allowing this is type-safety. If we were allowed to do the above, then we could have the following:
List<Double> ld;
wahey(ld);
Inside the method wahey, we could add some Strings to the input list (as the parameter maintains a List<Object> reference). Now, after the method call, ld refers to a list with a type List<Double>, but the actual list contains some String objects!
This seems different to the normal way Java works without generics. For instance:
Object o;
Double d;
String s;
o = s;
d = (Double) o;
What we are doing here is essentially the same thing, except this will pass compile-time checks and only fail at run-time. The version with Lists won't compile.
This leads me to believe this is purely a design decision with regards to the type restrictions on generics. I was hoping to get some comments on this decision?
What you are doing in the "without generics" example is a cast, which makes it clear that you are doing something type-unsafe. The equivalent with generics would be:
Object o;
List<Double> d;
String s;
o = s;
d.add((Double) o);
Which behaves the same way (compiles, but fails at runtime). The reason for not allowing the behavior you're asking about is because it would allow implicit type-unsafe actions, which are much harder to notice in code. For example:
public void Foo(List<Object> list, Object obj) {
list.add(obj);
}
This looks perfectly fine and type-safe until you call it like this:
List<Double> list_d;
String s;
Foo(list_d, s);
Which also looks type-safe, because you as the caller don't necessarily know what Foo is going to do with its parameters.
So in that case you have two seemingly type-safe bits of code, which together end up being type-unsafe. That's bad, because it's hidden and therefore hard to avoid and harder to debug.
Consider if it was...
List<Integer> nums = new ArrayList<Integer>();
List<Object> objs = nums
objs.add("Oh no!");
int x = nums.get(0); //throws ClassCastException
You would be able to add anything of the parent type to the list, which may not be what it was formerly declared as, which as the above example demonstrates, causes all sorts of problems. Thus, it is not allowed.
They aren't subtypes of each other due how generics work. What you want is to declare your function like this:
public void wahey(List<?> list) {}
Then it will accept a List of anything that extends Object. You can also do:
public void wahey(List<? extends Number> list) {}
This will let you take in Lists of something that's a subclass of Number.
I'd recommend you pick up a copy of "Java Generics and Collections" by Maurice Naftalin & Philip Wadler.
There are essentially two dimensions of abstraction here, the list abstraction and the abstraction of its contents. It's perfectly fine to vary along the list abstraction - to say, for instance, that it's a LinkedList or an ArrayList - but it's not fine to further restrict the contents, to say: This (list which holds objects) is a (linked list which holds only numbers). Because any reference that knows it as a (list which holds objects) understands, by the contract of its type, that it can hold any object.
This is quite different from what you have done in the non-generics example code, where you've said: treat this String as if it were a Double. You are instead trying to say: treat this (list which holds only numbers) as a (list which holds anything). And it doesn't, and the compiler can detect it, so it doesn't let you get away with it.
"What we are doing here is essentially
the same thing, except this will pass
compile-time checks and only fail at
run-time. The version with Lists won't
compile."
What you're observing makes perfect sense when you consider that the main purpose of Java generics is to get type incompatibilities to fail at compile time instead of run time.
From java.sun.com
Generics provides a way for you to
communicate the type of a collection
to the compiler, so that it can be
checked. Once the compiler knows the
element type of the collection, the
compiler can check that you have used
the collection consistently and can
insert the correct casts on values
being taken out of the collection.
In Java, List<S> is not a subtype of List<T> when S is a subtype of T. This rule provides type safety.
Let's say we allow a List<String> to be a subtype of List<Object>. Consider the following example:
public void foo(List<Object> objects) {
objects.add(new Integer(42));
}
List<String> strings = new ArrayList<String>();
strings.add("my string");
foo(strings); // this is not allow in java
// now strings has a string and an integer!
// what would happen if we do the following...??
String myString = strings.get(1);
So, forcing this provides type safety but it also has a drawback, it's less flexible. Consider the following example:
class MyCollection<T> {
public void addAll(Collection<T> otherCollection) {
...
}
}
Here you have a collection of T's, you want to add all items from another collection. You can't call this method with a Collection<S> for an S subtype of T. Ideally, this is ok because you are only adding elements into your collection, you are not modifying the parameter collection.
To fix this, Java provides what they call "wildcards". Wildcards are a way of providing covariance/contravariance. Now consider the following using wildcards:
class MyCollection<T> {
// Now we allow all types S that are a subtype of T
public void addAll(Collection<? extends T> otherCollection) {
...
otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T)
}
}
Now, with wildcards we allow covariance in the type T and we block operations that are not type safe (for example adding an item into the collection). This way we get flexibility and type safety.

Categories