generics with different types - java

sorry, this question might be very simple but I didn't find the answer in the internet.
public class Main {
public static void main(String[] args) {
int a = 1;
double b=2;
max(a,b);
}
public static <E> void max(E first , E second)
{
System.out.println(first);
System.out.println(second);
}}
when we pas the first parameter an integer then E is set to Integer and then we pass a double to it. we should get a compile error. (because E is Integer) but the program runs correctly and the output is
1
2.0
so what is my mistake?

If you hover over the method call in Eclipse, you'll see:
<? extends Number> void Main.max(? extends Number first, ? extends Number second)
i.e. the compiler infers the type of the generic type parameter as something that extends Number. Therefore, both Integer and Double, which extend Number are valid arguments for the max method.
If you print the types of the arguments passed to the max method:
public static <E> void max(E first , E second)
{
System.out.println(first);
System.out.println(second);
System.out.println (first.getClass ());
System.out.println (second.getClass ());
}
you'll see that an Integer and a Double were passed to the method:
1
2.0
class java.lang.Integer
class java.lang.Double

Java's type inference algorithm finds the most specific type that your arguments share. In this case, it should be Number.

Based on my understanding, there are two things. one is generic class and other one is generic method. In both the cases, you can pass any type of value (regardless of type) you can pass any type of parameter. Now when you are creating an object of specific generic lass type like MyClass<Integer> its no more generic, you can expect compiler error while doing operation with different type of parameter. But when you have some method like which adds element to a List<E>, you can add anything to this list, you will not get any compilation error.

Related

Unable to add value to a java generic list

Here's my sample code:
public class MyList<T extends Number> {
private List<T> items;
public void func() {
items.add(Integer.valueOf(1));
}
}
I think I should be able to add integer to items, but compilation fails:
Required type: T
Provided: Integer
Anyone knows what's wrong here?
Let us consider a more complete version of your example:
public class MyList<T extends Number> {
private List<T> items = new ArrayList<>();
public void func() {
items.add(Integer.valueOf(1));
}
}
Suppose for the sake of argument that the compiler says that is OK.
And now we will create an instance and call the func method:
MyList<Double> myDoubles = new MyList<>();
myDoubles.func();
Here is what happens.
We create a MyList instance where T is Double. That's OK: the Double class implements the Number interface.
The items has a notional type of List<Double> and we initialize it with an ArrayList. So we now have a list what should only contain Double values.
In the call to func we attempt to add an Integer to the List<Double>. That is wrong!
That is what the compilation error is saying with the Required type: T Provided: Integer message.
To spell it out, the compiler expects a value whose type is the type that T is going to be at runtime. But you have given it Integer. While Integer implements the Number interface, it is not necessary the same as what T will be at runtime. That is the root cause of your compilation error.
So what is the solution?
Well it depends on what the (actual) problem that this example is intended to solve. If you want item to be able to hold any Number, you should change
private List<T> items = new ArrayList<>();
to
private List<Number> items = new ArrayList<>();
and items.add(Integer.valueOf(1)) should work.
On the other hand, if you want to add 1 to items as an instance of the runtime type of T, that is much more difficult. The problem is that the code of MyList (as written) does not and cannot know what that type is! So, you need to EITHER pass the T instance representing 1 as a parameter to func OR pass a Class<T> parameter to func or the constructor and use reflection to create the instance of that class to represent 1.
But if you want something to auto-magically convert the Integer to what ever the actual runtime type of T is ... that is not possible.
When you're using a so-called bounded type parameter <T extends Number> means that type T is restricted by an upper bound expected to be a Number or one of its subtypes.
There are plenty of options if you would think about subtypes of Number: BigDecimal, AtomicLong, etc. That mean that your List<T> at runtime might appear to be a List<AtomicLong> and since behavior of generic types is invariant we would not be able to add anything that is not of type AtomicLong into such list (no Strings, no Integers, etc.).
Therefore, compiler would disallow to add an Integer into a List<T>, where T can be anything that extends Number (or the Number itself), because it can't be sure that it's type-safe.

Conflict between return type and method parameter when return type is 'T' and method parameter consist of wild card

I'm trying to run a code. i and i get two compilation errors:
1.Reference to System.out.println is ambiguous (conflict between method that gets char[] and a method that gets a String)
2.Cap#1 can't converted to T return st.pop()
import java.util.*;
public class Test
{
public static void main(String[] args)
{
Stack <Number> stackNumber = new Stack<Number>();
Test t = new Test();
t.setMethod(stackNumber,new Integer(3));
System.out.println(t.getMethod(stackNumber));
}
public <T extends Number> void setMethod (Stack<? super Number>st,T t)
{
st.add(t);
}
public <T>T getMethod (Stack<? extends Number >st)
{
return st.pop();
}
}
I know that i can change getMethod signature to return Number and program will be compiled successfully but i want to understand why with current signature i'm getting compilation errors?
AFAIK, T without bounds considered as Object and a function that declares to return Object can return any Object since Object is the "Father" of all classes (including Number). Can someone me what i'm dismissing here?
Neither of your methods should be using wildcard captures, you have two methods that are generic against some T. Like,
public <T> void setMethod(Stack<T> st, T t) {
st.add(t);
}
public <T> T getMethod(Stack<T> st) {
return st.pop();
}
If you want to ensure that T must be a Number for some reason (I would just use Number then), you define it at T. Like,
public <T extends Number> void setMethod(Stack<T> st, T t) {
st.add(t);
}
public <T extends Number> T getMethod(Stack<T> st) {
return st.pop();
}
but i want to understand why with current signature i'm getting compilation errors?
The errors are both because <T> is determined at the call site.
Looking at compilation error 1:
Java selects the most specifically-applicable method. Any of the PrintStream.println methods that take a reference-typed parameter could be selected.
From JLS 15.12.2.5:
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time error.
Anything that you can pass to println(char[]) or println(String) can also be passed to println(Object), therefore the former methods are more specific than the latter. As such, these will be selected in preference to println(Object).
However, some things that can be passed to println(char[]) cannot be passed to println(String), therefore neither of those is more specific than the other, hence the ambiguous method call.
Now looking at compilation error 2:
public <T>T getMethod (Stack<? extends Number >st)
{
return st.pop();
}
This method must be safe to invoke in all situations. You invoke it like this:
System.out.println(t.getMethod(stackNumber));
i.e. you treat the result simply like an object. But you could, legally, write this at the call site:
String s = t.getMethod(stackNumber);
It's hopefully clear that this would fail, because something popped out of a stack containing numbers can't be cast to a String.
Because the compiler can't guarantee that it will be called with a "safe" T, it's an error.

Java Generics won't compile

public class GenMethodDemo{
public GenMethodDemo(){
Sum.<Integer,Integer,Integer>sum(1,2);
}
public static void main(String args[]){
new GenMethodDemo();
}
}
class Sum{
public static final <S extends Number,Z extends S,X extends S> S sum(Z v1,X v2){
System.out.printf("v1=%1$s,v2=%2$s%n",v1.getClass(),v2.getClass());
return v1+v2;
}
Error get:
error: bad operand types for binary operator '+'
return v1+v2;
first type: Z
second type: X
where Z,S,X are type-variables:
Z extends S declared in method <S,Z,X>sum(Z,X)
S extends Number declared in method <S,Z,X>sum(Z,X)
X extends S declared in method <S,Z,X>sum(Z,X)
1 error
Can't understand what i'm doing wrong? If i change S.Z.X with Integer - all works fine but why with generics code won't compile?
Refactored code to:
public class GenMethodDemo2{
public GenMethodDemo2(){
Sum.<Integer>sum(1,2);
}
public static void main(String args[]){
new GenMethodDemo2();
}
}
class Sum{
public static final <S extends Integer> S sum(S v1,S v2){
System.out.printf("v1=%1$s, v2=%2$s%n",v1.getClass(),v2.getClass());
return v1+v2;
}
}
error: incompatible types: int cannot be converted to S
return v1+v2;
where S is a type-variable:
S extends Integer declared in method <S>sum(S,S)
1 error
So, S supposed to be an Integer or any subclass of Integer class, in any way it definitely should be possible to + their values. What's wrong with this version?
S extends Integer but int cannot be converted to S, how it could be? Why there is no autoboxing?
The problem you're experiencing is because there is no + operator defined for Number, only specific subclasses of Number. For example, + is defined for Integer, Double etc, but not BigInteger, BigDecimal or any other non-standard implementation of Number.
There is no good way to do generic addition. You end up having to provide a BinaryOperator<S>, so your code looks like:
sum(1, 2, Integer::sum);
sum(1.0, 2.0, Double::sum);
which is more verbose than just:
1 + 2
1.0 + 2.0
The compiler requires + to be defined for the compile-time types of v1 and v2. It doesn't matter if they are Integer (or whatever) at runtime: the decision as to whether to allow the + is made by the compiler, because it has to be able to guarantee that the method is type-safe for any arguments.
The method above is compiled to this:
public static final Number sum(Number v1, Number v2){
System.out.printf("v1=%1$s,v2=%2$s%n",v1.getClass(),v2.getClass());
return v1+v2;
}
This is called type erasure.
If + isn't defined for a Number, this code isn't allowed.
As a general solution for all inbuilt Number extensions:
public static Number sum(final Number a, final Number b) {
return new BigDecimal(a.toString()).add(new BigDecimal(b.toString()));
}
(Note: there's no guarantee that toString() will give a String which is parseable by BigDecimal but it does for all the inbuilt JDK Number extensions to the best of my knowledge.)
If you wanted to do something more clever, you could do some checks with instanceof to find the types of the inputs and work from there, but I tried that once for implementing Comparable between all Numbers and the performance wasn't any better than casting to BigDecimal.

When is ti appropriate to use declare generic argumens for the methods instead of simply usinf super class? [duplicate]

As far as I know, using an upper bounded generic and using a superclass as a method parameter both accept the same possible arguments. Which is preferred, and what's the difference between the two, if any?
Upper bounded generic as parameter:
public <T extends Foo> void doSomething(T foo) {}
Superclass as parameter:
public void doSomething(Foo foo) {}
That's an upper bounded type parameter. Lower bounds are created using super, which you can't really do for a type parameter. You can't have a lower bounded type parameter.
And that would make a difference, if you, for example want to pass a List<T>. So, for the below two methods:
public <T extends Foo> void doSomething(List<T> foos) {}
public void doSomething(List<Foo> foo) {}
And for the given class:
class Bar extends Foo { }
The following method invocation:
List<Bar> list = new ArrayList<Bar>();
doSomething(list);
is valid for 1st method, but not for 2nd method. 2nd method fails because a List<Foo> is not a super type of List<Bar>, although Foo is super type of Bar. However, 1st method passes, because there the type parameter T will be inferred as Bar.
Generally, you only need a type variable when it's used in more than one place in class/method/field declarations. When you declare one on a method (rather than a class), the only places to use it are on the parameters and return value of that method.
For example, you can use it on multiple parameters to ensure their types match:
public static <T> void addToList(List<T> list, T element) {
list.add(element);
}
This is a trivial example, but you can see that it prevents you from giving it an element that doesn't match the list's generic type:
List<Integer> list = new ArrayList<>();
addToList(list, 7);
//addToList(list, 0.7); // doesn't compile
//addToList(list, "a"); // doesn't compile
You can also declare a parameter and the return type to be the same type:
public static <T> T nullCheck(T value, T defValue) {
return value != null ? value : defValue;
}
Since this method is returning one of the two T objects it's given, we can safely say that the returned object is also of type T.
Integer iN = null;
Integer i = nullCheck(iN, 7);
System.out.println(i); // "7"
Double dN = null;
Double d = nullCheck(dN, 0.7);
System.out.println(d); // "0.7"
Number n = nullCheck(i, d); // T = superclass of Integer and Double
System.out.println(n); // "7"
As for the example in the question, the type variable is only being used once, so it's equivalent to using the superclass. In this case you should avoid declaring a type variable, it's just unnecessary clutter.
Also I should note that the other answer changes the example to use List<T> and List<Foo>, but as mentioned in the comments, the superclass is really List<? extends Foo>, so no type variable is needed there, either.

What is the value of T when method parameter type is <? extends T> in Java Generics?

I tried below code and little confused with generics. Can you please provide info on what is the value substituted for 'T' in the below method?
Code:
public class testT{
public static void main(String[] args){
List<Number> numbers1 = new ArrayList<>();
numbers1.add(5.4);
numbers1.add(2);
numbers1.add(44444444l);
getClass(numbers1);
}
static <T> void getClass(Collection<? extends T> collection) {
for(T c : collection) {
System.out.println(c.getClass().getName());
}
}
Outputs:
java.lang.Double
java.lang.Integer
java.lang.Long
The <T> before your method return type indicates a generic method.
In turn, you can reference T in your method parameters, which you are doing: your only parameter is a Collection of types equals to T or extending it (the idiom ? extends T defines the upper bound as T and uses a wildcard).
The collection you pass to your getClass method in your main method is a generic collection of Numbers, in which you add a Double, an Integer and a Long (object wrappers of numerical primitives extend Number).
These types are inferred and boxed by the actual values you are passing through add.
Since the getClass method resolves at runtime, you are printing the specific types of your collection elements.

Categories