What are potential risks of creating generic array with varargs in Java? - java

I found a way to create generic array inline with varargs and anonymous inner class:
import java.util.Arrays;
import java.util.Objects;
public class GenericArrayCreate<C> {
public void method() {
C[] ans = new Object() { C[] c(int length, C ... cs) { return Arrays.copyOf(cs, length); }}.c(10);
System.out.println(ans.length);
System.out.println(Objects.toString(ans));
}
public static void main(String[] args) {
new GenericArrayCreate<Class<? extends Integer>>().method();
}
}
Java compiler produces no any warning on this code, while code inspection (in IntelliJ) says:
Unchecked generics array creation for varargs parameter
What is happening here and why is it swearing?

Internally you are creating an array of "Object" instead of a array of "C"
Try this code :
public static <C> C[] method(int number) {
return new Object() {
C[] create(int length, C ... cs) {
return Arrays.copyOf(cs, length);
}
}.create(number);
}
public static void main(String[] args) {
System.out.println(Main.<Integer>method(10));
System.out.println(new Integer[10]);
}
[Ljava.lang.Object;#6bc7c054
[Ljava.lang.Integer;#232204a1
As you can see they are not the same.
It is dangerous because if you run something like these:
public static void main(String[] args) {
Integer[] integerArray1 = Main.<Integer>method(10);
Integer[] integerArray2 = new Integer[10];
}
And you will have a ClassCastException
[Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer
If you want to create any kind of array you need to send the class to construct the array, you can do it like this:
public static <C> C[] method(Class<C> clazz, int number) {
return (C[]) Array.newInstance(clazz, number);
}
public static void main(String[] args) {
Integer[] integerArray1 = Main.<Integer>method(Integer.class, 10);
Integer[] integerArray2 = new Integer[10];
}

Code inspection will note that because varargs are implemented with array, and array do not support generics. But you can suppress that warning by adding this:
#SuppressWarnings("unchecked")
Reason why this happens is it can lead to loss of information. You can read more about that here.

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>());

Is there any opportunity to use Class <?, ?> for my projects?

I hava some problems with my code.
First of all I have an interface:
public interface Generator <T, R> {
public T next (R x);
}
Then, I have created the class "DemoClass"
public class DemoClass {
private int id;
public DemoClass (int id){
this.id = id;
}
}
And... Generic Class as well
public class GenericClass implements Generator <DemoClass, Integer> {
public DemoClass next(Integer x) {
return new DemoClass (x);
}
}
After, I have created Main Class and a generic static method that containts a method like Class . I would like to know, is there any opportunity to use such construction like Class ???
My MainClass
import java.util.*;
public class MainClass {
private static Random rand = new Random ();
public static <T> T [] arr (T [] a, Class <?> typeToken){
try{
Generator <?, ?> gen = (Generator <?, ?>)typeToken.newInstance(); // How can I pass two paramets to Generator <?, ?>???
for (int i=0; i!=a.length; i++){
a[i] = (T) gen.next(rand.nextInt(100)); // This line does not work!
}
} catch (Exception e){
throw new RuntimeException (e);
}
return a;
}
public static void main (String [] args){
DemoClass [] myarr = arr (new DemoClass[10], GenericClass.class);
}
}
If you want to make that line work, you could do this:
public static <T> T[] arr(T[] a, Class<? extends Generator<T, Integer>> typeToken) {
try {
Generator<T, Integer> gen = typeToken.newInstance();
for (int i = 0; i != a.length; i++) {
a[i] = gen.next(rand.nextInt(100));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return a;
}
Basically, the bound on Class makes it so that what is returned by newInstance is a Generator<T, Integer>.
How can I pass two paramets to Generator ???
There is no need to pass any type parameters. Since Java generics use erasure, the concrete generic type arguments are not needed at runtime.
If you are using Java 8, I would suggest using a Supplier instead of a Class, since it allows you to create an instance without having to deal with the exceptions:
public static <T> T[] arr(T[] a, Supplier<? extends Generator<T, Integer>> typeToken) {
Generator<T, Integer> gen = typeToken.get();
for (int i = 0; i != a.length; i++) {
a[i] = gen.next(rand.nextInt(100));
}
return a;
}
...
// passing reference to constructor of GenericClass
DemoClass[] myarr = arr(new DemoClass[10], GenericClass::new);
No.
Generics work at compile time only. At runtime, all of the instances of class are exactly the same, the information of the parametrized types gone.
This is known as type erasure and it was designed that way to ensure that Java 1.4 binaries were compatible with Java 5 VM.
You can get a lot of info about this by searching "type erasure java" in the search box; for example Avoiding Java Type Erasure

Java ArrayList implementation of geneirics

I was going through java source code for ArrayList i came across below code
#SuppressWarnings("unchecked")
private E[] newElementArray(int size) {
return (E[]) new Object[size];
}
Here we trying to cast object[] to any E which we use when we declare Array List.
So i wrote code to test this as below :
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
class A {
}
class CheckBox extends A {
}
public class Box<T> {
public T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
public static void main(String[] args) {
Box<Integer> box = new Box<Integer>();
List<Integer> intList = new ArrayList<Integer>();
box.newElementArray(10);
Box[] a = (Box[]) new Object[10];
}
// prefer wild card as it does not allow to corrupt your data types
public void wildCard(Set<?> test) {
test.add(null);
}
private T[] newElementArray(int size) {
System.out.println(" casting object to t");
return (T[]) new Object[size];
}
}
Surprisingly line box.newElementArray(10); works fine but
Box[] a = (Box[]) new Object[10];
does not work , though both do same thing. Can anybody let me know what could be reason behind it or my understanding is wrong?
Because of type erasure, Java compiler replaces E with Object. So the method newElementArray(int size) becomes
private Object[] newElementArray(int size) {
return (Object[]) new Object[size];
}
which makes its invocation successful.
You can refer to http://docs.oracle.com/javase/tutorial/java/generics/genMethods.html for the details.

creating a generic static method in java

I wanted to create a static method which prints the contents of an array.I wrote one for String[] as below
public static void print(String[] a){
for(String x : a){
System.out.print(x+", ");
}
System.out.println();
}
I thought I could create a method which takes in a generic type ,and modified the code as below
public class ArrayPrinting<E> {
public static void printArray(E[] a){
for(E x : a){
System.out.print(x+", ");
}
System.out.println();
}
public static void main(String[] args) {
String[] a = {"A","B","C","D","E"};
}
}
But,this gives a compiler error
'Cannot make a static reference to the non-static type E'
So,how do I create such a method?or is it impossible ? Since this is a static method, I wonder how I can invoke the method without creating an instance. A call like
ArrayPrinting<E>.printArray(a) doesn't look right ..
Can someone help?
Try this
public class ArrayPrinting {
public static <E> void printArray(E[] a){
for(E x : a){
System.out.print(x+", ");
}
System.out.println();
}
public static void main(String[] args) {
String[] a = {"A","B","C","D","E"};
ArrayPrinting.printArray(a);
}
}
Ravi already covered the proper syntax for a generic method. I just want to point out that this particular method doesn't need to be generic:
public static void printArray(Object[] a) {
for (Object x : a) {
System.out.print(x + ", ");
}
System.out.println();
}
The reason this works is array covariance - a String[] is an Object[].
Class's generic type parameters are for class level variables and methods (instance variables and methods).So you can't use it.
You can handle it by declaring type parameter in the method itself:
public static <E> void printArray(E[] a){
.............
}
public class ArrayPrinting<E> {
public void printArray(E[] a){
for(E x : a){
System.out.print(x+", ");
}
System.out.println();
}
public static void main(String[] args) {
String[] a = {"A","B","C","D","E"};
new ArrayPrinting().printArray(a);
}
}

Type Erasure in Generics

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.

Categories

Resources