Type Erasure in Generics - java

What does the following code look at runtime after type erasure:
public class Test<T> {
T lst;
List<T> list1;
void meth() throws InstantiationException, IllegalAccessException{ T res = (T)lst.getClass().newInstance();}
static <S> void meth(S t){}
}
class TestUse{
public static void main(String[] args) {
Test<Integer> gint = new Test<Integer>();
Test<String> gstr = new Test<String>();
gint.meth();
gstr.meth();
}

The following piece of code would not work:
T res = (T)lst.getClass().newInstance();
Since T is of type Object at runtime in all the possible cases.
One workaround is using generics with inheritance:
public abstract class Generic<T> {
public abstract Class<T> getConcreteClass();
public void doSomething() {
Class<T> clazz = getConcreteClass();
T t = clazz.newInstance();
doSomethingWithT();
}
}
public class ConcreteClass extends Generic<YourObject> {
public Class<YourObject> getConcreteClass() {
return YourObject.class;
}
}

I think it looks like this
public class Test {
Object lst;
List list1;
void meth() throws InstantiationException, IllegalAccessException{
Object res =lst.getCla ss().newInstance();
}
static void meth(Object t){
}
}
class TestUse{
public static void main(String[] args) {
Genrics gint = new Genrics();
Genrics gstr = new Genrics();
gint.meth();
gstr.meth();
}
although I do not think this would compile as it stands. There is no Genrics class - or is that the Test class.

Assuming that your Genrics class is actually your Test class, it should be equivalent to something like this:
public class Genrics {
Object lst;
List list1;
void meth() throws InstantiationException, IllegalAccessException {
Object res = lst.getClass().newInstance();
}
static void meth(Object t){}
}
class TestUse{
public static void main(String[] args) {
Genrics gint = new Genrics();
Genrics gstr = new Genrics();
gint.meth();
gstr.meth();
}
Whether my assumption is right or wrong, the way to think about this is that all generic type parameters are simply erased from your code and replaced with the most specific bound for the type parameter (Object in your case) or nothing where appropriate. Casts are introduced where needed.
The details of how type erasure works are spelled out in the Java Language Specification.

Related

confusion with Java wildcards

Please explain to me why I have one example that compiles and the other one doesn't compile
This is example that compiles
import java.util.ArrayList;
public class MyClass1 <T> {
public ArrayList<MyClass1<?>> lst;
public MyClass1()
{
lst = new ArrayList<MyClass1<?>>();
}
public static void main(String[] args) {
M(new MyClass1<Double>());
}
public static <T1> void M(MyClass1<T1> t1)
{
var d0 = new MyClass1<Double>();
d0.lst.add(t1);
}
}
But that doesn't compiles
import java.util.ArrayList;
public class MyClass1 <T> {
public ArrayList<?> lst;
public MyClass1()
{
}
public static void main(String[] args) {
M(Double.valueOf(1.1));
}
public static <T1> void M(T1 t1)
{
var d0 = new MyClass1<Double>();
d0.lst.add(t1); // error — java: incompatible types: T1 cannot be converted to capture#1 of ?
}
}
Why in the first case I have wildcard, and everything is OK, but in the second case I have wildcard and it doesn't compile
The second snippet can not be compiled because the generics in Java are about type safety(type guarantee). Thus, if we are declearing List<Integer> the compiler is convinced, that the list will contain only integers. But when we have List<?> (read this like list of some type), compiler cant predict what will the list contain. Today we are adding Integers, tomorrow somebody else will add a ServerSocket. In runtime if we will try to get a value from such a list, we will get a ClassCastException. That is why this code cant even be compiled - to prevent such situations. Read about type erasure and bridge methods.
In your concrete case, i guess, you would like to have something like this:
public class MyClass<T> {
public ArrayList<T> lst;
public static void main(String[] args) {
var obj = new MyClass<Double>();
obj.foo(1.1);
}
public void foo(T s) {
lst.add(s);
}
}
The question mark also can be useful:
static void m(List<? extends Shape> list) {
for (Shape el : list) {
// we are not quite interested what type is this list of.
}
}
And the usage example:
m(new ArrayList<Circle>());
m(new ArrayList<Shape>());

Incompatible types Void and Object - Java Generics

Below code throws
"Incompatible types. Required Sample<Void> but create was inferred to Sample<T>. Incompatible equality constraints:Void and Object".
public class SampleClass{
public static <T> Sample<T> create(String str) {}
}
Sample<Void> sample = SampleClass.create("abc");
It actually does work.
Full example:
public class SampleClass {
public static void main(String[] args) {
Sample<Void> sample = SampleClass.create("abc");
}
public static <T> Sample<T> create(String str) {
return null;
}
private static class Sample<T> {
}
}
Compiles and executes just fine (with JDK 11).

How to deliver the class of a generic type to a method in Java?

I want to implement a class that instantiates generic types.
public class DisjointSet<T extends Set<E>, E> {
private final Class<T> setType;
public DisjointSet(Class<T> setClass) {
this.setType = setClass;
}
public void doSomething(E Element) {
T set = setClass.newInstance();
set.add(element);
}
}
I tried instantiating the class like this:
DisjointSet<HashSet<Integer>, Integer> disjointSet = new DisjointSet<>(HashSet<Integer>.class);
However using .class on a generic type does not seem to be allowed. How would I correctly pass the required Class of a generic type to the constructor?
Not sure it is good to expose the inner set type (Hash versus other) in the parameterized type.
Actually due to type erasure you can't instantiate parameterised types directly, but you can pass in a factory,
package langGenerics;
import java.util.HashSet;
import java.util.Set;
public class UseGenerics {
public static void main(String[] args) {
SetFactory<Integer> setFactory = HashSet::new;
DisjointSet<Integer> disjointSet = new DisjointSet<>(setFactory);
disjointSet.doSomething( 123 );
}
}
interface SetFactory<T> { Set<T> get(); }
class DisjointSet<T> {
private SetFactory<T> setFactory;
public DisjointSet(SetFactory<T> setFactory) {
this.setFactory = setFactory;
}
public void doSomething(T item) {
Set<T> set = setFactory.get();
set.add(item);
}
}
If you really want to init your own set storage, then I suggest you to pass Supplier to your constructor:
public static class DisjointSet<T extends Set<E>, E> {
T set;
public DisjointSet(Supplier<T> supplier) {
set = supplier.get();
}
public void doSomething(E element) {
set.add(element);
}
}
Then use it:
DisjointSet<HashSet<Integer>, Integer> set = new DisjointSet<>(HashSet::new);
if this is what you wanted,
public class DisjointSet<T extends Set<E>, E> {
private final Class<T> setType;
public DisjointSet(Class<T> setClass) {
this.setType = setClass;
}
public static void main(String[] args) {
DisjointSet<HashSet<Integer>, Integer> disjointSet = new DisjointSet(new HashSet<Integer>().getClass());
}
}

Generic Return type for collections

Is is possible to have a generic return type such that collection can built with interface as well as implementation.
public static void main(String[] args) {
List<Test1> list = ImmutableList.of(new Test1());
List<ITest> test_return_1 = buildIntRange(list);
List<Test1> test_return_2 = buildIntRange(list); //error
}
private static <K extends ITest> List<ITest> buildIntRange(List<K> test) {
return ImmutableList.copyOf(test);
}
Sure. Use the same signature as ImmutableList.copyOf, or just use ImmutableList.copyOf directly:
static <T> List<T> copyOf(Iterable<? extends T> collection)

How to do a "new" of a generic class T extends AbstractClass (JAVA)

I'm trying to instantiate a generic class called "T" extending "AbstractLauncher" and I didn't understand all topic I saw on google. Could you help me ?
I've got several class called ConcretXLauncher and I would not have on my MainClass "ConcretXLauncher" but only generics who could be whatever extending AbstractLauncher...
public MainClass < T extends AbstractLauncher > {
public MainClass(Config config){
//T launcher = new T(config); doesnt work, I want to do new ConcretXLauncher(config)
T launcher = newInstance(????);
// code using "launcher"
}
public static < T > T newInstance(Class clazz) {
return clazz.newInstance() ;
}
}
In other topics, I saw this function but I don't know how to call it ?
What do I have to put for "clazz" argument ?
Here's an example of how to define your newInstance method and call it:
public class Test {
public static <T> T newInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException {
return clazz.newInstance();
}
static class A {
#Override
public String toString() {
return "hello";
}
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
A a = newInstance(A.class);
System.out.println(a);
}
}
You could use the following
public class MainClass < T extends AbstractLauncher> {
public MainClass(Config config, Class<T> clazz) throws Exception {
T launcher = newInstance(clazz);
// code using "launcher"
}
public static <T> T newInstance(Class<T> clazz) throws Exception {
return clazz.newInstance() ;
}
}
you could introduce a custom runtime exception rather than throwing Exception from the constructor

Categories

Resources