I have wrote the following code:
public class Test
{
public static void main(String args[]) throws ParseException
{
System.out.println(new Generic<Integer>("one").type); //outputs "one"
}
}
class Generic<T>
{
public T type;
public Generic(Object obj)
{
type = (T)obj;
}
}
And i thought i will get an exception while doing the cast, but i didnt. I get the output: "one". But if i do new generic<Integer>, type become a variable of type Integer, so how can i cast the String "one" to T and store it in the variable type in my generic class without getting an exception? An explanation would be great.
There is no exception because type erasure removes any checking of the Integer type from your code. Since println takes Object the compiler doesn't need to insert a cast, and the code simply erases to:
System.out.println(new Generic("one").type);
Try the following assignment instead:
Integer i = new Generic<Integer>("one").type;
In that case you'll get a ClassCastException because the code erases to:
Integer i = (Integer)new Generic("one").type;
Notice that switching the types behaves differently. This will throw a ClassCastException:
System.out.println(new Generic<String>(123).type);
That's because the println(String) overload is used, so the code erases to:
System.out.println((String)new Generic(123).type);
This is because of java type erasure:
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
basically java will replace T with Object (if T has no bounding upper type like in your example)- and everything is fine at runtime.
It is not casting your string to integer....because it is unbound. It is converting the T to object instead and in the sysout you are getting obj.toString instead.
The proper way would be as below and automatically you will get compile exception in your test class... as expected.
class Generic<T>
{
public T type;
public Generic(T t)
{
type = t;
}
}
class Generic<T>
{
public T type;
public Generic(T obj)
{
type = obj;
}
}
There. Fixed it for you.
Related
This question already has an answer here:
How to check a (unchecked) cast in java?
(1 answer)
Closed 3 years ago.
I want to write universal function, which can make an object of any type by name and return it as its own type, not as Object type.
import java.lang.reflect.Constructor;
public class Test{
static <T extends Object> T makeObject(String className) {
try {
Class<?> c = Class.forName(className);
Constructor<?> constructor = c.getConstructor();
Object object = constructor.newInstance();
return (T)c.cast(object);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
//without casting
String obj = makeObject("java.lang.String");
System.out.println(obj.getClass());
}
}
It works fine, but I get the warning
Type safety: Unchecked cast from capture#4-of ? to T
What to do to fix the problem without adding #SuppressWarnings("unchecked")?
If you want this to be safe you need to pass the class as a parameter, otherwise there is no way you're going to get what you want. Your code will compile whatever I ask the return type to be, but will fail at runtime if the type isn't compatible.
Try it out:
String s = makeObject("java.lang.String"); // compiles and runs
Integer i = makeObject("java.lang.String"); // compiles but fails at runtime with java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
A safe version would look like that
static <T> T makeObject(Class<T> clazz) throws Exception {
return clazz.getConstructor().newInstance();
}
but that suggests you already know what type you want to have instead of just getting a class name from a file or something.
Going through generics at generics and got this question
public class Box<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
If i want box to hold only integer i can initiate like below . Makes sense here.
Box<Integer> integerBox = new Box<Integer>();
case 2:-
But at various place i have seen following below pattern
public class Box<Integer> {
private Integer var;
public void set(Integer var) { this.var = var; }
public Integer get() { return var; }
}
The way of instantiation is same i.e
Box<Integer> integerBox = new Box<Integer>();
But how instantiating with integer helps here as we are not using it ?
I am not getting what benefit we are getting with generic specifier along with class declaration in second case?
I mean if i declare class without diamond operator in second case, what the benefit i am losing ? I am sure i am but not hitting the right spot .
Edit :-
Looks like i was not clear in case 2 snippet. Please refer below code snippet
public class Box<Integer> {
private String var;
public void set(String var) { this.var = var; }
public String get() { return var; }
}
What you see at "various places" is just bad coding. Integer in
public class Box<Integer>
is not a reference to the java.lang.Integer class, but a generic type variable, just like T was. As such, it shadows java.lang.Integer type inside the class definition. In fact, the two snippets you posted are exactly the same.
Just run the following piece of code, if you are not convinced:
public class Demo {
public static void main(String args[]) {
Box<String> a = new Box<>();
a.set("dummy");
a.whatami();
}
}
class Box<Integer> {
private Integer var;
public void whatami() {
System.out.println(var.getClass().getName());
}
public void set(Integer var) { this.var = var; }
public Integer get() { return var; }
}
Output will be java.lang.String, not java.lang.Integer.
In fact, you are not required to name the generic type T. You can call it Foo, Bar, Baz. Maybe avoiding misleading names could be a good idea. Maybe.
EDIT: your edit completely changes the question. In that specific case, you get no advantage from declaring a generic that does not use the generic type variable. Neither you get any disavantage, because of compile time type erasure.
But, it's not completely pointless. Although the class does not use the type tag now, it might use it in a future version. And if you get to the a point where you need to make your class generic, you will have to go through all the code you wrote until that moment and amend it with the diamond notation. It will cost you time and you might also have to refactor certain parts.
I had an interview test and saw the following code:
EDIT:
public class TestValue {
private Value<SomeValue> defaultValue;
#Test
public void Empty_Value_Has_No_Value() {
Assert.assertFalse(Value.<SomeValue> createEmptyValue()
.hasValue());
}
#Test
public void Default_Value_IsEmpty() {
Assert.assertEquals(Value.<SomeValue> createEmptyValue(),
defaultValue);
}
#Test
public void Non_Empty_Value_Has_Value() {
Assert.assertTrue(new Value<SomeValue>(true, new SomeValue())
.hasValue());
}
}
I had never seen Java generic like
Value.<SomeValue>
The test is to implement Value class with the given unit test code above.
I tried to figure out the Value method signature below (need implementation):
public interface Value<T> {
public boolean hasValue();
public Value<T> createEmptyValue();
}
Any one know, please help?
Thank you
EDIT: Should be like this according to answers below #marlon
public class Value<T> {
public boolean hasValue(){}
public static <M> Value<M> createEmptyValue(){}; //need <M>
}
The key syntax to know:
Value.<SomeValue> //ClassName.<Type>method
is way to invoke static method of a class with parameterized argument.
EDIT: according to #snipes83, syntax to invoke non-static method of a class with parameterized argument.
SomeObject.<Type>method
Value.<SomeValue> it's the way generics are represented for methods.
Using Google Guava's Optional as an example:
Optional<String> email = Optional.<String>of(strEmail);
See Generic Types - Invoking generic methods
Since interfaces cannot declare static methods (shame on you java), just declare your method as static and forget about the interface, like this:
class Value<T> {
public static <T> Value<T> createEmptyValue(){
return null;
}
}
Look at the class Test with the method getEmptyList below:
public class Test {
public <T> List<T> getEmptyList() {
return new ArrayList<T>();
}
}
It returns an empty List containing objects of type T.
If you use Test like this
Test t = new Test();
List<Integer> list = t.getEmptyList();
Then the type inference mechanism is able to infer the type parameter based on the variable type.
However if you need to use the return value of getEmptyList within a method invocation expression like in the following example where the method printList expects a single argument of type List<Integer>, then the type can not be infered from any variable type.
public void printList(List<Integer> list) {
for (int i : list) {
System.out.print(i);
}
}
printList(t.getEmptyList()); // This will FAIL.
In this case you need to specify the type using the following:
printList(t.<Integer>getEmptyList());
1) This is how generic methods are invoked. Refer >> http://docs.oracle.com/javase/tutorial/java/generics/methods.html
2) <SomeValue> in Value.<SomeValue> is optional. Compiler can infer the type. This is called TypeInference. Refer >> http://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html
Answer Updated:
3) Value.<SomeValue> createEmptyValue() is right and Value.<SomeValue>createEmptyValue() is right too. Both ways are legal. Just tested it. Didn't notice before.
Although Value is itself obviously typed ( based on the instance variable type of Value<SomeValue>), the static createEmptyValue() method is also typed.
A reasonable assumption, if naming conventions have been adhered to, is that SomeValue extends (or implements) Value.
Although there us no one correct answer, a likely possibility for the signature of Value is:
public class Value<T extend Value> {
public static <V extends Value> V createEmptyValue() {
// some impl
}
}
I don't understand why this confuses the compiler. I'm using the generic type T to hold an object that's not related to the put and get methods. I always thought GenericClass and GenericClass<Object> were functionally identical, but I must be mistaken. When compiling the DoesntWork class I get incompatible types - required: String - found: Object. The Works class does what I expect. What's going on here?
public class GenericClass<T> {
public <V> void put(Class<V> key, V value) {
// put into map
}
public <V> V get(Class<V> key) {
// get from map
return null;
}
public static class DoesntWork {
public DoesntWork() {
GenericClass genericClass = new GenericClass();
String s = genericClass.get(String.class);
}
}
public static class Works {
public Works() {
GenericClass<Object> genericClass = new GenericClass<Object>();
String s = genericClass.get(String.class);
}
}
}
The thing about how raw types work -- generic types that you've left out the arguments for -- is that all generics for them and their methods are erased as well. So for a raw GenericClass, the get and put methods also lose their generics.
This is because when you work with a generic class without the extra type information you work with what is sometimes called the degenerate form of the class. The degenerate form has ALL generic type information removed.
Essentially - your class becomes something like:
public class GenericClass {
public void put(Class key, Object value) {
// put into map
}
public Object get(Class key) {
// get from map
return null;
}
...
}
The compiler response you are seeing is therefore expected behaviour.
It's mentioned in a Java Puzzlers.
When writing a generic method to process data for a form, I came across with the following (as I see it) unexpedted behavior. Given the following code:
public class Test {
public <T> void someGenericMethod(Integer a) {
#SuppressWarnings("unchecked")
T t = (T) a;
System.out.println(t);
System.out.println(t.getClass());
}
public static void main(String[] args) {
Test test = new Test();
test.<BigDecimal>someGenericMethod(42);
}
}
AFAIK, the code above should generate a ClassCastException in the line T t = (T) a because the method call in main is setting the parametrized type to BigDecimal and casting from Integer to BigDecimal is not allowed, conversely to what I expected, the program executed well and printed the following:
42
class java.lang.Integer
In fact, if I add another parameter to the method signature (like String b) and make another assigment T t2 = (T) b, the program prints
42
class java.lang.String
Why the t variable changed it's type to Integer (is, by any chance, making some kind of promotion on the type T to Object)?
Any explanation on this behavior is welcome
(T) a is an unchecked cast: due to type erasure, the runtime has no way of knowing what type T is, so it can't actually check if a belongs to type T.
The compiler issues a warning when you do this; in your case, you've suppressed that warning by writing #SuppressWarnings("unchecked").
Edited to add (in response to a further question in the comments below):
If you want to check the cast, you can write this:
public class Test {
public <T> void someGenericMethod(Class<T> clazz, Integer a) {
T t = clazz.cast(a);
System.out.println(t);
System.out.println(t.getClass());
}
public static void main(String[] args) {
Test test = new Test();
// gives a ClassCastException at runtime:
test.someGenericMethod(BigDecimal.class, 42);
}
}
by passing in clazz, you allow the runtime to check the cast; and, what's more, you allow the compiler to infer T from the method arguments, so you don't have to write test.<BigDecimal>someGenericMethod anymore.
Of course, the code that calls the method can still circumvent this by using an unchecked cast:
public static void main(String[] args) {
Test test = new Test();
Class clazz = Object.class;
test.someGenericMethod((Class<BigDecimal>) clazz, 42);
}
but then that's main's fault, not someGenericMethod's. :-)
When compiling, your code above basically becomes the following non-generic method:
public void someGenericMethod(Integer a) {
Object t = a;
System.out.println(t);
System.out.println(t.getClass());
}
There is no cast. No exception.
You specify a type parameter in your method signature, but never use it.
I think you want something like this:
public class Test {
public <T> void someGenericMethod(T someItem) {
System.out.println(someItem);
System.out.println(someItem.getClass());
}
}
public static void main(String[] args) {
Test test = new Test();
BigDecimal bd = new BigDecimal(42);
test.someGenericMethod(42); // Integer
test.someGenericMethod("42"); // String
test.someGenericMethod(42L); // Long
test.someGenericMethod(bd); // BigDecimal
}
Note that there's no need to cast.
The parameter type is declared in the method signature and inferred from the parameter.
In your code you're parameterizing the method call (which I've never seen) and passing in an int.
It's kinda hard to understand what you're trying to do, since your example code does nothing.