how type variable allowing wrong type? - java

package org.my.java;
public class TestTypeVariable {
static <T,A extends T> void typeVarType(T t, A a){
System.out.println(a.getClass());
System.out.println(t.getClass());
}
public static void main(String[] s){
int i= 1;
typeVarType("string", i);
}
}
when run, following is the output :
class java.lang.Integer
class java.lang.String
How can A be of type Integer when it has been already upper-bounded to String?
Please explain me on it.

Two things here:
there is a simple solution to the "bad" typing: T isn't String but Object. And Integer extends Object. But please note: this only works with the "enhanced" type inference capabilities of Java8. With Java7, your input will not compile!
misconception on your end: getClass() happens at runtime, and therefore returns the specific class of the objects passed - independent on what the compiler thinks about generics at compile time.

Related

Why won't this code with Java generics compile?

Consider the following code:
import java.math.BigDecimal;
class Scratch {
public static class Test<N extends Number> {
public void foo(Class<N> numberClass) {
System.out.println(numberClass);
}
public void bar() {
foo(BigDecimal.class);
}
}
public static void main(String[] args) {
Test<Number> t = new Test<>();
t.bar();
}
}
This fails to compile on line 12 (the call to foo()) with incompatible types: java.lang.Class<java.math.BigDecimal> cannot be converted to java.lang.Class<N>. I don't get it, because the generic N extends Number, and so I should be able to pass BigDecimal.class to a method which takes a Class<N> parameter. TIA for any thoughts!
Suppose a caller did:
var t = new Test<Integer>();
t.bar();
then, bar() would pass a BigDecimal to foo(), which expects an Integer.
The short answer really is that BigDecimal is not necessarily N. It is a little confusing because BigDecimal is within the <N extends Number> bounds, but the current instance's generic type argument can be any type that extends Number, but the bar method assumes BigDecimal.
Check this slightly modified version of the method:
public void bar() {
new Test<BigDecimal>().foo(BigDecimal.class);
}
That method compiles. Why? Becuase Test<BigDecimal>() has BigDecimal as type argument.
When you call foo(BigDecimal.class), you're assuming that this was instantiated with BigDecimal as type argument, which is not always true, and the the compiler is preventing a bug.
The error in your code would be similar to a hypothetical bar() method in ArrayList<T> that does add("string"), which would be wrong for the same reason (the actual array list instance may be created to hold integers, not strings, so the inside code shouldn't make assumptions)

Why does reflection work when used in a generic method?

I read that generics in Java are implemented using type erasure.
I wanted to test that and see if I fully understood the concept, but I was surprised and confused to find out that this piece of code worked perfectly:
public class Test{
public static void main(String []args){
canAssign3(3,3.2);
}
static<T1,T2> boolean canAssign3(T1 from, T2 to){
Class<?> c1 = from.getClass();
Class<?> c2 = to.getClass();
System.out.println(c1.getName());
System.out.println(c2.getName());
System.out.println(c2.isAssignableFrom(c1));
return (c2.isAssignableFrom(c1) );
}
}
Output:
java.lang.Integer
java.lang.Double
false
I was expecting c1's class and c2's class to be Object, and also c2.isAssignableFrom(c1) to be true.
What am I missing here?
Generic or not, each object has a Class and it's the method on that class you're invoking.
This method would work just as effectively if both parameters were of type Object. In fact, making them generic adds no value, because you're not using the generic type.

Java Generics - calling specific methods from generic-typed ones

I try to dive deeply into the Java Generics and I've come across a problem described by the following sample code.
public static void test(Object o) {
System.out.println("Hello Object!");
}
public static void test(Integer i) {
System.out.println("Hello Integer!");
}
public static <T> void test(Collection<T> col) {
for (T item : col) {
System.out.println(item.getClass().getSimpleName());
test(item);
}
}
public static void main (String[] args) throws java.lang.Exception
{
Collection<Integer> ints = new ArrayList<>();
ints.add(1);
test(ints);
}
The output of the sample is
Integer
Hello Object!
All the types are obviously known at compile time. As far as I understand, Java holds only a single compiled copy of each method (unlike C++) and because no other constraints are given about the parameter T, it's forced to call the generic Object implementation.
My question is - is there any way to call the "Hello Integer" method for integers without overloading the test method for Collection<Integer> and without using runtime type checking?
It cannot be done. Due to type erasure type Collection<T> will be resolved to type Collection<Object>. Erasure of Generic Methods
You can observe the same behavior without generics.
Object o = new Integer(5);
test(o); // will output "Hello Object!"
Overloaded methods are resolved using compile-time rather than run-time type information. See JLS: https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.9
No, you can't do what you ask for, for the reasons you gave. Type erasure means that the overload is resolved once for all type variables, and it has to resolve to Object.

Type inference in generic method & generic class

Having trouble understanding generic programming in Java.
I read some tutorial about it but still quite confused, especially when things get complicated.
Can anyone explain what's happening in this example?
import java.util.Date;
public class Test1 {
public static void main(String[] args) {
P<Cls> p = new P<>(); //<1> //I expect a ClassCastException here, but no. Why? //How does the type inference for class P<E> work?
System.out.println(p.name); //it prints
// System.out.println(p.name.getClass());//but this line throws ClassCastException //why here? why not line <1>?
test1(p);//it runs
// test2(p);//throws ClassCastException//What is going on in method test1&test2?
//How does the type inference for generic methods work in this case?
}
public static<T> void test1(P<? extends T> k){
System.out.println(k.name.getClass());
}
public static<T extends Cls> void test2(P<? extends T> k){
System.out.println(k.name.getClass());
}
}
class P<E>{
E name = (E)new Date();//<2>
}
class Cls{}
P<Cls> p = new P<>();
Remember that Java implements generics by erasure, meaning that the constructor for P doesn't really have any idea what E is at runtime. Generics in Java are purely there to help developers at compile-time.
This means that when you create a new P<>(), a new Date() is created, but it isn't actually cast to any particular type, because the runtime doesn't know anything about E. E does not exist at runtime, as far as the P<E> class is concerned. name is just an Object reference that has a Date inside. However, any time you write code that consumes name in a way where the runtime environment needs to know that it's of a particular type (Cls in this case), the compiler inserts a cast to that type without telling you.
p.name.getClass() gets compiled as ((Cls)p.name).getClass(), which will create a class cast exception.
test2() specifies a type constraint that is not generic (extends Cls). So its call to p.name.getClass() likewise is translated to ((Cls)p.name).getClass().
On the other hand:
System.out.println(p.name) is actually the same as System.out.println((Object)p.name) because println is a non-generic method that takes an object.
test1(p.name) is similar. Because the runtime doesn't actually know what type T is, it basically casts p.name as Object before calling getClass() on it.
In other words, here's your code as it actually gets compiled:
class P{
Object name = new Date();
}
public static void main(String[] args) {
P p = new P();
System.out.println(p.name);
System.out.println(((Cls)p.name).getClass());
test1(p);
test2(p);
}
public static void test1(P k){
System.out.println(k.name.getClass());
}
public static void test2(P k){
System.out.println(((Cls)k.name).getClass());
}

Using the addition operator on a generic subclass of Number

Can someone tell me why this code gives me a compile-error?
public class Main {
public static void main(String[] args) {
System.out.println(sum(2, 6.9));
}
public static <T extends Number<T>> T sum(T a, T b) {
T result = a + b; // compile-error here
return result;
}
}
Number is not a generic class, so you can't parameterize it:
public abstract class Number implements java.io.Serializable {
...
}
Further, the + operator only works on primitive types like int, long, etc., rather than Number subtypes like Integer, Long, etc. (EDIT: It will operate on these by unboxing, yes, but it cannot automatically box the result in the appropriate wrapper class.)
(You've stumbled upon one of the reasons Number is a poor example of polymorphism. It really only performs object-to-primitive conversions.)
You can, instead create an interface with
public interface ALU <T extends Number> {
public T add(T a, T b);
}
And make your main class implementing the interface created.
public class Main implements ALU <Integer>.
And create the method add inside your main class.
public Integer add(Integer a, Integer b){
return a + b;
}
And this will work.
The error The operator + is undefined for the argument type(s) T, T is caused because The type Number is not generic; it cannot be parameterized with arguments <T>
The error highlighting in eclipse provides this information, not sure if other IDE's provide the same information.
You have to use:
a.doubleValue()+b.doubleValue()
as Number is a class and does not support the operator +

Categories

Resources