I have a method below doing casting on a String according to the given type, assuming the String given must be correct.
private static <T> T parsePrimitive(final Class<T> primitiveType, final String primitiveValue) {
if (primitiveType.equals(int.class) || primitiveType.equals(Integer.class)) {
return primitiveType.cast(Integer.parseInt(primitiveValue));
}
/*
...
for the rest of the primitive type
...
*/
}
However, when I call parsePrimitive(int.class, "10");,
primitiveType.cast(Integer.parseInt(primitiveValue));
This causes ClassCastException, any idea for this?
p.s. In fact, when I use Object as the return type, and no casting in before return, it works fine outside the method, but this is not generic enough I think.
Thanks in advance for any help.
You are mixing up autoboxing and casting. The java compiler will generate bytecode to box and unbox your primitives to objects and vice versa, but the same does not apply to types.
Boxing/Unboxing = variables
Casting = types
In your particular case, int.class and Integer.class are not assignable from each other.
Class<?> intClazz = int.class;
Class<?> integerClazz = Integer.class;
System.out.println(intClazz);
System.out.println(integerClazz);
System.out.println(integerClazz.isAssignableFrom(intClazz));
Output:
int
class java.lang.Integer
false
With the amount of specialized checks you would have to put in your logic I am not sure its worth trying to come up with a generic method for parsing a String into a primitive value.
int.class is a VM internal class and not the same thing as Integer.class. Here's a small snippet of code to show the differences between int.class and Integer.class.
import java.lang.reflect.Modifier;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
dump(int.class);
System.out.println("---");
dump(Integer.class);
}
private static void dump(Class<?> c) {
System.out.printf(
"Name: %s%n" +
"Superclass: %s%n" +
"Interfaces: %s%n" +
"Modifiers: %s%n",
c.getName(),
c.getSuperclass() == null ? "null" : c.getSuperclass().getName(),
Arrays.asList(c.getInterfaces()),
Modifier.toString(c.getModifiers()));
}
}
Outputs:
Name: int
Superclass: null
Interfaces: []
Modifiers: public abstract final
---
Name: java.lang.Integer
Superclass: java.lang.Number
Interfaces: [interface java.lang.Comparable]
Modifiers: public final
Related
I am stuck in generics downcasting. Because I learn type erasure. When code compiles all parameterized types are converted to the upper bound if the bound is not defined then it changes to object.
Gen Class
public class Gen<T>
{
T ob;
Gen(T o)
{
ob = o;
}
T getob()
{
return ob;
}
void showType()
{
System.out.println("Type of T is: " + ob.getClass().getName());
}
}
GenDemo Class
public class GenDemo
{
public static void main(String [] args)
{
Gen<String> strob = new Gen<String>("I am Ahmad");
String str = strob.getob();
System.out.println("Value of str is: " + str);
}
}
String str = strob.getob(); is converted to String implictly. how JVM converted strob.getob() to String. From where JVM found the strob.getob() is downcast to string. Because type erasure changes the return type to object. So in byte code the return type of getob() is object.But when I call getob() it automatically downcast to string.So I am very confusing the downcasting in generic please explain me in detail.
Yes, at runtime the return type of strob.getob(); is basically equivalent to just Object.
However, during compilation the compiler understands that strob is a Gen<String> and ensures that all the necessary low-level operations are added. So it basically treats that line as if you had written
String str = (String) strob.getob();
In other words: it will generate the bytecode for the type cast, even though that typecast is nowhere to be found in the source code.
This also explains when for some reason the generic type system is broken (by using unchecked casts, basically): if for some reason getob() actually returns something that can't be cast to String then you'll get a boring old ClassCastException just as if you had tried to cast an Integer to String.
The following code is working fine for m2() but is throwing a ClassCastException when I use m1().
The only difference between m1 and m2 is the number of arguments.
public class Test {
public static void m1() {
m3(m4("1"));
}
public static void m2() {
m3(m4("1"), m4("2"));
}
public static void m3(Object... str) {
for (Object o : str) {
System.out.println(o);
}
}
public static <T> T m4(Object s) {
return (T) s;
}
public static void main(String[] args) {
m1();
}
}
My question is - Does varargs not work with a single argument when we use generics?
PS : This is not related to ClassCastException using Generics and Varargs
Let's skip the fact that you ignored an unchecked cast warning for now and try to understand why this happened.
In this statement:
Test.m3(Test.m4("1"));
There is one inferred type, which is the return type of m4. If one is to use it outside the m3 invocation context, as in:
Test.m4("1"); // T is Object
T is inferred as Object. One can use a type witness to force the compiler to use a given type:
Test.<String>m4("1"); // T is String
...or by using the expression in an assignment context:
String resString = Test.m4("1"); // T is String
Integer resInt = Test.m4("1"); // T is Integer <-- see the problem?
... or in an invocation context:
Integer.parseInt(Test.m4("1")); // T is String
Long.toString(Test.m4("1")); // T is Long
Now, back to Test.m3(Test.m4("1"));: I couldn't find a reference for this, but I believe the compiler is forced to make T resolve to the parameter type of m3, which is Object[]. This means that T, which has to coincide with the parameter type of m3, is therefore resolved to Object[], and that makes it as though you specified generic types as:
Test.m3(Test.<Object[]>m4("1")); // this is what is happening
Now, because m4 is not returning an Object[], m3 is receiving a String, which leads to the inescapable ClassCastException.
How to solve it?
The first way to fix this is to specify a correct type argument for m4:
Test.m3(Test.<String>m4("1"));
With this, String is the return type of m4, and m3 is called with a single String object (for the Object... var-arg), as if you had written:
String temp = m4("1");
m3(temp);
The second approach was suggested in #Ravindra Ranwala's deleted answer. In my opinion, this boils down to heeding compiler warnings:
public static <T> T m4(Object s) {
return (T) s; // unchecked cast
}
The unchecked cast warning simply tells you that the compiler (and the runtime) are not going to enforce type compatibility, simply because T is not known where you cast. The following version is type-safe, but it also makes the compiler use String as the return type of m4 as well as the type of the parameter to m3:
public static <T> T m4(T s) {
return s;
}
With this, m3(m4("1")); still uses Object... as the parameter type of m3, while keeping String the return type of m4 (i.e., a string value is used as the first element of the Object array).
Because in the method implementation the array is only read and nothing is stored in the array. However, if a method would store something in the array it could attempt to store an alien object in the array, like putting a HashMap<Long,Long> into a HashMap<String,String>[]. Neither the compiler nor the runtime system could prevent it.
Here is another example that illustrates the potential danger of ignoring the warning issued regarding array construction in conjunction with variable argument lists.
static <T> T[] method_1(T t1, T t2) {
return method_2(t1, t2); // unchecked warning
}
static <T> T[] method_2( T... args) {
return args;
}
public static void main(String... args) {
String[] strings = method_1("bad", "karma"); // ClassCastException
}
warning: [unchecked] unchecked generic array creation of type T[] for
varargs parameter
return method_2(t1, t2);
As in the previous example, the array's component type is non-reifiable and due to type erasure the compiler does not create a T[] , but an Object[] instead. Here is what the compiler generates:
Example (same a above, after translation by type erasure):
public final class Test {
static Object[] method_1( Object t1, Object t2) {
return method_2( new Object[] {t1, t2} ); // unchecked warning
}
static Object[] method_2( Object[] args) {
return args;
}
public static void main(String[] args) {
String[] strings = (String[]) method_1("bad", "karma"); // ClassCastException
}
}
The unchecked warning is issued to alert you to the potential risk of
type safety violations and unexpected ClassCastExceptions
In the example, you would observe a ClassCastException in the main() method where two strings are passed to the first method. At runtime, the two strings are stuffed into an Object[]; note, not a String[] .
The second method accepts the Object[] as an argument, because after type erasure Object[] is its declared parameter type. Consequently, the second method returns an Object[] , not a String[] , which is passed along as the first method's return value. Eventually, the compiler-generated cast in the main() method fails, because the return value of the first method is an Object[] and no String[]
Conclusion
It is probably best to avoid providing objects of non-reifiable types where a variable argument list is expected. You will always receive an unchecked warning and unless you know exactly what the invoked method does you can never be sure that the invocation is type-safe.
You have to use a Class instance of T to cast since the generic type erasure during compilation
public class Test {
public static void m1() {
m3(m4("1", String.class));
}
public static void m2() {
m3(m4("1", String.class), m4("2", String.class));
}
public static void m3(final Object... str) {
for (Object o : str) {
System.out.println(o);
}
}
public static <T> T m4(final Object s, Class<T> clazz) {
return clazz.cast(s);
}
public static void main(String[] args) {
m1();
m2();
}
}
$java Test
1
1
2
Varargs and Generics don't mix to well in Java. This is because
Varags implemented by having an array of the respective type at runtime (array of Object in your case)
Arrays and Generics are just incompatible. You can't have an Array of String-Lists.
In Java, consider the following piece of code:
int myPrimitiveInt = 5;
Integer myObjectInt = 4;
Object fromPrimitive = myPrimitiveInt;
Object fromObject = myObjectInt;
System.out.println(fromPrimitive.getClass());
System.out.println(fromObject.getClass());
System.out.println(int.class);
And the output:
class java.lang.Integer
class java.lang.Integer
int
What I would like, is a way to also get the output int for the first println.
WHY?, you will ask. Well, for one thing, I would just like to know if something like this is even possible.
But the actual practical reason behind this is an abstraction layer for testing private methods with primitive type arguments via reflection. The minimal code:
package testing;
import java.lang.reflect.Method;
public class Testing {
private static void doStuff(int a) {
System.out.println("primitive: " + ((Object) a).getClass());
}
public static void main(String[] args) throws ReflectiveOperationException {
Reflect.reflect(Testing.class, "doStuff", 10);
}
}
abstract class Reflect {
static Object reflect(Class<?> clazz, String methodName, Object arg) throws ReflectiveOperationException {
Method method = clazz.getDeclaredMethod(methodName, arg.getClass());
method.setAccessible(true);
return method.invoke(null, arg);
}
}
The output:
Exception in thread "main" java.lang.NoSuchMethodException: testing.Testing.doStuff(java.lang.Integer)
at java.lang.Class.getDeclaredMethod(Class.java:2130)
at testing.Reflect.reflect(Testing.java:17)
at testing.Testing.main(Testing.java:11)
Expected output:
primitive: class java.lang.Integer
Or even better (if possible at all):
primitive: int
Note: I know I can do clazz.getDeclaredMethod(methodName, int.class). The whole point of this post is to make this procedure more abstract. Please do not give me answers suggesting to pass the argument types to the reflect method!
What happens when you write Object x = 10
The int is autoboxed, which makes it into an Integer with value 10.
Why can't this be detected afterwards
Because there is no difference between the Integer with value 10 that was autoboxed and another Integer with value 10
How can I get around this
You need to add separate methods to overload for primitive values, these can handle the primitive values, so they do not get autoboxed.
A primitive type is never "cast" to an object.
A primitive may be "autoboxed" into an object, but you can never determine this in code, since autoboxing is just code that is added by the compiler and is indistinguishable from the same code that you might have added by hand.
I have the following generic test class:
public class BrokenGenerics<T> {
private T[] genericTypeArray;
public BrokenGenerics(T... initArray) {
genericTypeArray = initArray;
}
public void setArray(T[] newArray) {
genericTypeArray = newArray;
}
public T get(int idx) {
return genericTypeArray[idx];
}
public Class getType() {
return genericTypeArray.getClass().getComponentType();
}
public static boolean breakThis(BrokenGenerics any) {
any.setArray(new B[]{new B(2)});
return false;
}
public static void main(String[] args) {
BrokenGenerics<A> aBreaker = new BrokenGenerics<A>(new A("1"));
System.out.println(aBreaker.get(0));
System.out.println(aBreaker.getType());
breakThis(aBreaker);
System.out.println(aBreaker.get(0));
System.out.println(aBreaker.getType());
}
private static class A {
public String val;
public A(String init) {
val = init;
}
#Override
public String toString() {
return "A value: " + val;
}
}
private static class B {
public int val;
public B(int init) {
val = init;
}
#Override
public String toString() {
return "B value: " + val;
}
}
}
When I run it, I get this output, and no errors:
A value: 1
class BrokenGenerics$A
B value: 2
class BrokenGenerics$B
Now, I understand why this compiles; it can't know at compile-time that breakThis is being passed a generic of a bad type. However, once it runs the line any.setArray(new B[]{new B(2)});, shouldn't it throw a ClassCastException (NOTE THAT IT DOES NOT! Try it yourself!) because I'm trying to pass a B[] to a method that expects an A[]? And after that, why does it allow me to get() back the B?
After Type Erasure, T will be turned into Object since you didn't specify a bound on T. So, there is no problem at runtime assigning any type of array to genericTypeArray, which is now of type Object[] or calling the function setArray(...), which now also accepts an argument of type Object[]. Also, your get(...) method will simply return an Object.
Trouble starts when you access elements in the array with a wrong type expectation, since this might lead to (implicit or explicit) illegal type casts, for example by assigning the value returned by get(...) to a variable of type A.
You can also get a run-time ClassCastException if you try to type-cast the array itself, but, in my experience, that is a case that tends to come up less often, although it can be very obscure to find or even understand if it does happen. You can find some examples below.
All generics-checking happens only at compile-time. And if you use raw types, these checks can not be performed rigorously, and thus the best the compiler can do is to issue a warning to let you know that you are giving up an opportunity for more meaningful checks by omitting the type argument.
Eclipse with its standard settings (and probably the java compiler with the correct flags) shows these warnings for your code:
"Class is a raw type" where you define getType() (somewhat unrelated to your question)
"BrokenGenerics is a raw type" where you define breakThis(...)
"Type safety: The method setArray(Object[]) belongs to the raw type
BrokenGenerics" where you call setArray(...) inside breakThis(...).
Examples for causing ClassCastException due to illegal type-cast of the array:
You can get ClassCastExceptions at runtime if you expose the array to the outside world (which can often be a dangerous thing to do, so I try to avoid it) by adding the following to BrokenGenerics<T>:
public T[] getArray() {
return genericTypeArray;
}
If you then change your main method to:
BrokenGenerics<A> aBreaker = new BrokenGenerics<A>(new A("1"));
A[] array = aBreaker.getArray();
System.out.println(array[0]);
System.out.println(aBreaker.getType());
breakThis(aBreaker);
array = aBreaker.getArray(); // ClassCastException here!
System.out.println(array[0]);
System.out.println(aBreaker.getType());
You get the ClassCastException at runtime at the indicated position due to a cast of the array itself rather than one of its elements.
The same thing can also happen if you set the variable genericTypeArray to protected and use it from code that subclasses your generic class with a fixed type argument:
private static class C extends BrokenGenerics<A> {
public C(A... initArray) {
super(initArray);
}
public void printFirst() {
A[] result = genericTypeArray; // ClassCastException here!
System.out.println(result[0]);
}
}
To trigger the exception, add the following to you main method:
C cBreaker = new C(new A("1"));
cBreaker.printFirst();
breakThis(cBreaker);
cBreaker.printFirst();
Imagine this case coming up in a bigger project... How on earth would you even begin to understand how that line of code could possible fail?!? :) Especially since the stack trace might be of very little help trying to find the breakThis(...) call that is actually responsible for the error.
For more in-depth example cases, you can take a look at some tests I did a little while back.
shouldn't it throw a ClassCastException because I'm trying to pass a B[] to a method that expects an A[]?
No. As this post explains, your invocation of setArray in
public static boolean breakThis(BrokenGenerics any) {
any.setArray(new B[]{new B(2)});
return false;
}
is done on a reference expression of the raw type BrokenGenerics. When interacting with raw types, all corresponding generic parameters are erased. So setArray is actually expecting a Object[]. A B[] is a Object[].
why does it allow me to get() back the B?
Assuming you're asking about this
System.out.println(aBreaker.get(0));
PrintStream#println(Object) expects an Object, not an A. As such, there is no reason for the compiler to insert a cast here. Since there is no cast, there is no ClassCastException.
If you had instead done
A a = aBreaker.get(0);
or had a method like
void println(A a) {}
...
println(aBreaker.get(0));
then these would cause ClassCastException. In other words, the compiler will insert a cast (checkcast) anywhere a type needs to be converted from a generic type parameter. That was not the case with PrintStream#println.
Similarly,
System.out.println(aBreaker.getType());
doesn't even involve the generic parameter declared in BrokenGenerics
public Class getType() {...}
and also returns a value of the raw type Class. The compiler has no reason to add a checkcast to A.
I'm trying to make a function that will invoke the constructor of a class given a set of arguments
package testytest;
import java.lang.reflect.Constructor;
public class MainClass {
public static <T> T newClass(Class<?> inst, Object ... args){
#SuppressWarnings("unchecked")
Constructor<?> [] ctor = (inst.getDeclaredConstructors());
int argIndex = 0;
ctorLoop: for(Constructor<?> x : ctor){
argIndex = 0;
for(Class<?> s : x.getParameterTypes()){
if(argIndex > args.length || args[argIndex++].getClass() != s){
if(argIndex <= args.length)
System.out.println("Param doesnt match : " + args[argIndex-1].getClass() + " with " + s);
continue ctorLoop;
}
}
try{
return (T)x.newInstance(args);
}catch(Exception e){
System.err.println("Error in instantiating instance of class : " + inst);
return null;
}
}
System.err.println("No instance of constructor found for class " + inst);
return null;
}
public static void main(String[] args) {
System.out.println(newClass(Double.class,5.0));
}
}
which gives me the error
Param doesnt match : class java.lang.Double with double
Param doesnt match : class java.lang.Double with class java.lang.String
No instance of constructor found for class class java.lang.Double
looking at the line
Param doesnt match : class java.lang.Double with double
is there a way to natively make this boolean match without case swapping every native type (double,float,long,int,etc?)
There are constants defined in the wrapper classes that represent the Class objects for the primitive types. For double, use Double.TYPE.
The Class instance representing the primitive type double.
That should match the presumed double argument for the constructor you're looking for.
Other examples for other primitives are Integer.TYPE, Float.TYPE, Short.TYPE, Byte.TYPE, Long.TYPE, Character.TYPE, and Boolean.TYPE. There is even Void.TYPE for void.
No, there isn't. Both the reference type java.lang.Double and the primitive type double have corresponding Class objects. There is no way to go from one Class object to another (boxing/unboxing).
You'll have to keep your own (bi)map.
The easy way to do this is with java.beans.Statement. It handles all this conversion for you automatically.