What is the difference between generics - java

Ok, i read this question in this web-site and it did not answer me the main point.
Assume we have like this:
public <T extends Number> void someMethod(List<T> list)
AND
public void someMethod(List<? extends Number> list)
Consider that I do not need any lower bound or upper list bound. So rejection of these things, what is the difference between it in this situation? Because ? is List of unknown and T is List of T, but from one side they are similar in this situation (methods above).
(P.s. I know differences between it, but in this situation it looks for me very similar and as I consider it does not matter what to use.)

Here:
public <T extends Number> void someMethod(List<T> list)
You can make use of the unknown type T, in your code. Eg:
T obj = list.get(0);
list.add(obj);
This is not possible here:
public void someMethod(List<? extends Number> list)

List <? extends Number> means anything that extends Number here.
So assume you have classes like
public class Example<T extends Number> {
public <T extends Number> void someMethod(List<T> list) ...
public <T extends Number> void someOtherMethod(List<T> list) ...
}
public class SomeClass extends Number {
}
public class SomeClass2 extends Number {
}
and you created an instance like
Example<SomeClass> myExample = new Example<SomeClass>();
so when you want to use your methods here, the input parameters must be insances exactly of the same class SomeClass, we cant use SomeClass2 here. For example the following is invalid:
List<SomeClass2> someClass2 = new ArrayList<SomeClass2>();
example.someMethod(someClass2);
but if you use <? extends Number> as an input parameter to the someMethod then it is possible. You can pass anything that extends Number, someClass2 or 3 , 4 or any other class.

Related

Wildcard vs TypeParameter

class Employee<T extends Number> { // valid
}
class Employee<? extends Number> { // invalid
}
private static void test(List<? super Number> list1) { // valid
}
private static <T>void test(List<T super Number> list1) { // invalid
}
what exactly is difference between ? and T and when to use what?
Why with class definition, ? doesn't work but it works with List and why T works with class definition but not with List?
Where to declare a generic
You can not use a generic type token T before introducing it.
In your method example you try to declare the T at the wrong spot, that is invalid syntax. You have to introduce it beforehand.
For the class example however, you have put it in the right spot.
Here is where you can introduce your generic type token on a class wide level:
public class Foo< HERE > { ... }
and thats how you do it for a method only:
public < HERE > void foo(...) { ... }
Bounded generics
In both cases you can bound your T, like T extends Number and then use it accordingly:
public class Foo<T extends Number> { ... }
// or
public <T extends Number> void foo(...) { ... }
After you have introduced your T, you will use it just like that. So List<T>, as an example.
public <T extends Number> void foo(List<T> list) { ... }
Note that T super Number is invalid on as it makes little sense and does not provide more information than just T or Number or simply Object, depending on what you are trying to achieve. You can read more about that at Java generic methods: super can't be used?
Wildcards
Wildcards are a different thing. They are not a generic type token that you have to introduce first, such as T. Instead, they clarify the type range you want to accept.
For example a method like
public static void foo(List<? super Dog> list) { ... }
can be called with a List<Dog>, a List<Animal> or even a List<Object>. We call such a list a consumer of Dogs. To be precise, these are all lists that would accept a dog, so list.add(new Dog()) will work.
On the other side, we have
public static void foo(List<? extends Dog> list) { ... }
which can be called with a List<Dog> or also a List<Chihuahua>. We call such a list a producer (or provider) of Dogs. To be precise, these are all lists that can provide dogs. So Dog dog = list.get(0) will work.
You can read more about the details of what wildcards are and how they work at What is PECS (Producer Extends Consumer Super)?
When to use which?
In general, you would use a generic type token T when you actually still need to maintain the type safety throughout your code. I.e. when you need to be able to give the type a name. Otherwise you use wildcards ?.
For example, suppose you want to create a method that takes in a list and an element to add to it:
public static <T> void addToList(List<T> list, T element) {
list.add(element);
}
You need to introduce the T to ensure that the type of the list matches the given element. Otherwise someone could do addToList(dogs, cat), which you do not want.
If you do not have the need to actually name the type, you can also just use a wildcard. For example a method that takes a list and prints all its contents:
public static void printAll(List<?> list) {
for (Object object : list) {
System.out.println(object);
}
}

Incompatible types when using upper bounding wildcard

I'm really confused of how upper bounded types work in Java generics.
Let's say I have
interface IModel<T>
interface I
class A implements I
class B implements I
class C implements I
then I have a method with parameter as follows
foo(IModel<Map<? extends I, Map<? extends I, List<? extends I>>>> dataModel)
calling that method like
IModel<Map<A, Map<B, List<C>>>> model = ...
foo(model)
ends with compilation error
Error:(112, 49) java: incompatible types: IModel<java.util.Map<A,java.util.Map<B,java.util.List<C>>>> cannot be converted to IModel<java.util.Map<? extends I,java.util.Map<? extends I,java.util.List<? extends I>>>>
I have read docs about Java generics from the Oracle web, trying to google it, but there must be something I totally misunderstood.
This question can be shorted as why
foo(IModel<List<? extends I>> dataModel)
can not accept argument like
IModel<List<A>> model
Explanation
List<A> is a subtype of List<? extends I>, so it is ok:
public void bar(List<? extends I> list);
List<A> listA;
bar(listA);
But, it does not make IModel<List<A>> a subtype of IModel<List<? extends I>>, just like IModel<Dog> is not a subtype of IModel<Animal>, so the code you posted can not be compiled.
Solution
You can change it to:
foo(IModel<? extends Map<? extends I, ? extends Map<? extends I, ? extends List<? extends I>>>> dataModel)
or
<FIRST extends I, SECOND extends I, THIRD extends I> void foo(IModel<Map<FIRST, Map<SECOND, List<THIRD>>>> dataModel)
to make it compile.
First of all, I wonder how much effort it would have been for you (one person) to sort out the code to be in this form:
import java.util.List;
import java.util.Map;
interface IModel<T> {}
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
public class UpperBounds
{
public static void main(String[] args)
{
IModel<Map<A, Map<B, List<C>>>> model = null;
foo(model);
}
static void foo(IModel<Map<? extends I, Map<? extends I, List<? extends I>>>> dataModel)
{
}
}
instead of letting hundreds of people (who want to help you) do this on their own, in order to have something that they can compile and have a look at in their IDE. I mean, it's not that hard.
That being said: Technically, you're missing a few more extends clauses here. This compiles fine:
import java.util.List;
import java.util.Map;
interface IModel<T> {}
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
public class UpperBounds
{
public static void main(String[] args)
{
IModel<Map<A, Map<B, List<C>>>> model = null;
foo(model);
}
static void foo(IModel<? extends Map<? extends I, ? extends Map<? extends I, ? extends List<? extends I>>>> dataModel)
{
}
}
But you should
not
implement it like that. That's obscure. Whatever this dataModel parameter is, you should consider creating a proper data structure for that, instead of passing along such a mess of deeply nested generic maps.
The reason of why the original version did not compile was already mentioned in other answers. And it can be made clearer by showing an example using a much simpler method call. Consider this example:
interface IModel<T> {}
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
public class UpperBounds
{
public static void main(String[] args)
{
List<List<A>> lists = null;
exampleA(lists); // Error
exampleB(lists); // Works!
}
static void exampleA(List<List<? extends I>> lists)
{
}
static void exampleB(List<? extends List<? extends I>> lists)
{
}
}
The exampleA method cannot accept the given list, whereas the exampleB method can accept it.
The details are explained nicely in Which super-subtype relationships exist among instantiations of generic types? of the generics FAQ by Angelika Langer.
Intuitively, the key point is that the type List<A> is a subtype of List<? extends I>. But letting the method accept only a List<List<? extends I>> does not allow you to pass in a list whose elements are subtypes of List<? extends I>. In order to accept subtypes, you have to use ? extends.
(This could even be simplified further: When a method accepts a List<Number>, then you cannot pass in a List<Integer>. But this would not make the point of List<A> being a subtype of List<? extends I> clear here)
Having a method method1(Map<I> aMap>) and A being a class implementing I doesn't allow you to call the method with a Map<A> and that's for a reason.
Having the method:
public static void foo2(IModel<I> dataModel) {
System.out.println("Fooing 2");
}
Imagine this code:
IModel<A> simpleModel = new IModel<A>() {};
foo2(simpleModel);
This shouldn't work because you supply a more specific type to a method that requires a generic type. Now imagine foo2 does the following:
public static void foo2(IModel<I> dataModel) {
dataModel = new IModel<B>() {};
System.out.println("Fooing 2 after we change the instance");
}
Here you will try to set IModel to IModel which is valid - because B extends I, but if you were able to call that method with IModel it wouldn't work
Create your model like:
IModel<Map<I, Map<I, List<I>>>> model = ...
and in the corresponding maps and lists add objects of type A, B and C which will be valid and then call the function foo(model)

Using wildcards with nested generics in java

I have a class that can a list of take both a generic Number and a Double to perform a certain calculation.
This works fine:
public class Test<T extends Number> {
public void testGeneric(List<T> list){
doTest(list);
}
public void testExplicit(List<Double> list){
doTest(list);
}
public void doTest(List<? extends Number> testList){}
}
However, if the argument in question is a nested type, it doesn't compile anymore:
public class Test<T extends Number> {
public void testGeneric(List<List<T>> list){
doTest(list);
}
public void testExplicit(List<List<Double>> list){
doTest(list);
}
public void doTest(List<List<? extends Number>> testList){}
}
Note that the functionality of the list doesn't matter, the second List type can be anything generic, for instance a wrapper around the T type. I don't really see why there should be any difference. Is there a way around this?
Thanks!
As you apparently have realized, a List<Double> is not a subtype of List<Number>; you need the wildcards to make the inheritance "propagate" into the generic types: a List<Double> is a subtype of List<? extends Number>. However, the wildcards must go all the way from the outermost level: a List<X> is a subtype of List<List<? extends Number>> only if X is exactly a List<? extends Number>. If you want to accept other subtypes of List<? extends Number>, such as List<Double>, you need List<? extends List<? extends Number>>.

Raw types inside of generic definition

I wonder why the following generic definition does not produce a compiler warning:
class MyClass<T extends List> { }
and how the above definition is different to
class MyClass<T extends List<?>> { }
Whenever you read about generics, you read about how raw types should be avoided and consequently, whenever you handle generic types, you get a compiler warning. The raw type inside of the first definition does however not create such a warning.
Secondly, I wonder how the exact subtyping definition between raw types and generic types are. According to this summary, raw types are kind of an "opt-out" of type checking such that type checking is simply inactive whenever a raw type is involved. Is this assumption correct? And how does this effect the above "raw" generic definitions?
Thank you for your help!
UPDATE: I understand what you are saying. However, this is not what I am confused about. Look at this scenario:
class MyClass<T extends MyClass<T>> {}
public void callWildcard1(MyClass<?> node) {
callBound1(node);
}
public <T extends MyClass<T>> void callBound1(T node) {
// Do something
}
public void callWildcard2(MyClass<?> node) {
callBound2(node);
}
public <T extends MyClass> void callBound2(T node) {
// Do something
}
The first call from callWildcard1 to callBound1 is not allowed because of the generic constraint. The second is however allowed. How can I perform the first call without "inner raw types"? I don't see why the compiler would forbid the first. Shouln't any parameter valid wildcard parameter imply ? extends MyClass<?>?
UPDATE 2: I found out by trial and error, that I can solve the problem by defining:
public <T extends MyClass<? extends T> void callBound2(T node) {
// Do something
}
even though I do not quite understand why. But there is even more confusion, when looking at this example: (this is a very simple version of what I am actually trying to do.)
public void call() {
genericCall1(new MyFilter<MyClass<?>>(), MyClass.class);
genericCall2(new MyFilter<MyClass<?>>(), MyClass.class);
}
public <T extends MyClass<? extends T>> void genericCall1(MyFilter<T> filter, Class<? extends T> filterBoundary) {
// Do something.
}
public <T extends MyClass<? extends T>, U extends T> void genericCall2(MyFilter<T> filter, Class<? extends U> filterBoundary) {
// Do something.
}
class MyClass<T extends MyClass<T>> { }
class MyFilter<T extends MyClass<? extends T>> { }
Why is genericCall1 prohibited and genericCall2 is not? Again, I found the solution by an academic guess instead of true understanding. Sometimes, when working with Java and its generics I want to cry...
The difference is that when you use class MyClass<T extends List> { } inside MyClass you lose type safety.
for example:
class A <T extends List<?>>{
void someFunc(T t) {
t.add(new Object());//compilation error
}
}
class B <T extends List>{
void someFunc(T t) {
//compiles fine
t.add(new Object());
t.add("string");
t.add(new Integer(3));
}
}

Java generic wildcards: if something wants ? extends T, what's wrong with T?

protected List<? extends ErrorTO> errors = new ArrayList<ErrorTO>();
public abstract Collection<? extends ErrorTO> getErrors();
public void registerError(ErrorTO e) {
errors.add(e);
}
there is a compiling error of line "errors.add(e)", it is expecting some type of "? extends ErrorTO" and does not like ErrorTO? why and ho to fix that? Thanks!
The compiler doesn't know that getErrors will return a Collection<ErrorTo>; it might instead return a Collection<SomeSubclassOfErrorTo>. If you then tried to add an ErrorTo that wasn't of this subclass to the collection, type safety would be violated. For this reason, the compiler will not let you do this.
List<? extends ErrorTO> is not a List that can contain anything that extends ErrorTO.
It is a List of an (unspecified) subclass of ErrorTO.
if MyErrorTO extends ErrorTO, then I can create a List<MyErrorTO> and assign it to errors.
Your code will then try to add an ErrorTO to my List, which is illegal - my List can only contain a specific subclass of ErrorTO
You can't add anything to a List<? extends ErrorTO>. Make it a List<ErrorTO>.
List<? extends ErrorTO> means: a list of some unknow class, which is ErrorTO or extends ErrorTO. So obviously, if you could add something to it, its type-safety would be broken.
Maybe this will help a little. Lets say we have classes
class Fruit {}
class Pear extends Fruit{ void methodA(){} }
class Apple extends Fruit{ void methodB(){} }
and we want to create method like this
public static void method(List<? extends Fruit> list, Fruit f) {
list.add(f); //<-- why is this forbidden?
}
As list argument this method can accept lists like
ArrayList<Fruit>
ArrayList<Apple>
ArrayList<Pear>
If the argument would be new ArrayList<Pear>(); then compiler shouldn't allow to add elements like Fruit or Apple.
But compiler can't know what kind of list he will be dealing with. Because of that lack of knowledge it is impossible to add any elements via List<? extends Fruit> list reference.

Categories

Resources