I was wondering what is the difference between the following two method declarations:
public Object doSomething(Object obj) {....}
public <T> T doSomething(T t) {....}
Is there something you can/would do with one but not the other? I could not find this question elsewhere on this site.
Isolated from context - no difference. On both t and obj you can invoke only the methods of Object.
But with context - if you have a generic class:
MyClass<Foo> my = new MyClass<Foo>();
Foo foo = new Foo();
Then:
Foo newFoo = my.doSomething(foo);
Same code with object
Foo newFoo = (Foo) my.doSomething(foo);
Two advantages:
no need of casting (the compiler hides this from you)
compile time safety that works. If the Object version is used, you won't be sure that the method always returns Foo. If it returns Bar, you'll have a ClassCastException, at runtime.
The difference here is that in the first, we specify that the caller must pass an Object instance (any class), and it will get back another Object (any class, not necessarily of the same type).
In the second, the type returned will be the same type as that given when the class was defined.
Example ex = new Example<Integer>();
Here we specify what type T will be which allows us to enforce more constraints on a class or method. For example we can instantiate a LinkedList<Integer> or LinkedList<Example> and we know that when we call one of these methods, we'll get back an Integer or Example instance.
The main goal here is that the calling code can specify what type of objects a class will operate upon, instead of relying on type-casting to enforce this.
See Java Generics* from Oracle.
*Updated Link.
The difference is that with generic methods I don't need to cast and I get a compilation error when I do wrong:
public class App {
public static void main(String[] args) {
String s = process("vv");
String b = process(new Object()); // Compilation error
}
public static <T> T process(T val) {
return val;
}
}
Using object I always need to cast and I don't get any errors when I do wrong:
public class App {
public static void main(String[] args) {
String s = (String)process("vv");
String b = (String)process(new Object());
}
public static Object process(Object val) {
return val;
}
}
You don't need to do additional class casting. In first case you will always get an object of class java.lang.Object which you will need to cast to your class. In second case T will be replaced with the class defined in generic signature and no class casting will be needed.
At runtime, nothing. But at compile time the second will do type checking to make sure the type of the parameter and the type of the return value match (or are subtypes of) whatever type T resolves to (the first example also does type checking but every object is a subtype of Object so every type will be accepted).
T is a generic type. Meaning it can be substituted by any qualifying object at runtime. You may invoke such a method as follows:
String response = doSomething("hello world");
OR
MyObject response = doSomething(new MyObject());
OR
Integer response = doSomething(31);
As you can see, there is polymorphism here.
But if it is declared to return Object, you can't do this unless you type cast things.
in the first case it takes a parameter of any type e.g.string and return a type foo. In the second case it takes a parameter of type foo and returns an object of type foo.
There are few reasons that you can consider Generics over Object type in Java:
Generics is flexible and safe. At the same time, working with Object that requires type-casting is error-prone
Type Casting in Java is slow
ref : [1]: https://www.infoworld.com/article/2076555/java-performance-programming--part-2--the-cost-of-casting.html
Related
I see compiler allowing following method. But can anyone explain how the return type will be decided at compile time.And what will be the value of T.
Also can you give me reference document or video where I can understand further on how the generic type will be decided internally by java compiler.
public <T> T getValue(String abc) throws Exception {
if(abc.equals("x")){
return (T) new String[]{"x","y","z"};
} else {
return (T) "abc";
}
}
There is type erasure process at the compile time. From javadoc:
During the type erasure process, the Java compiler erases all type parameters and replaces each with its first bound if the type parameter is bounded, or Object if the type parameter is unbounded
So, in your examples all T will be replaces by Object type. But during the execution time it will be casted to the type you specified. For example:
Integer value = obj.<Integer>getValue("string");
There is no compile time error, but at the runtime it will try to apply casting return (Integer) "abc"; and you will get ClassCastException
See more about Erasure Generic type and Erasure of Generic Methods
You can't do this generically.
The best you can do is to use a non-generic method, returning a common superclass - in this case, Object:
public Object getValue(String abc) throws Exception {
if(abc.equals("x")){
return new String[]{"x","y","z"};
} else {
return "abc";
}
}
This places the burden onto the caller to cast the object to the appropriate type, which you might balk at. But note that your current method also does that, because there is nothing to stop them from writing this:
String s = getValue("x");
String[] arr = getValue("x");
Both of these are fine, but one of them will fail at runtime. The caller has to somehow know which of the invocations will return a String, and which will return a String[].
The advantage of forcing them to do the casting is that then they can see the cast, and know that they are going to have to take care.
A better approach would be to return a value of the same type in all cases: return new String[]{"abc"} instead.
I have a java interface like this
public interface MyInterface<T> {
public <V extends T> V get(String key, Bundle bundle);
}
please note the <V extends T> type parameter of the method.
Then I have a class MyFoo implements MyInterface
class MyFoo implements MyInterface<Object> { // Object because can be any type
#Override public <V> V get(String key, Bundle bundle) {
return new Other();
}
}
So when I now have a class like this:
class Bar {
public Other other;
public Other setOther(Other other);
}
Then I want to take MyFoo to set Other in a Bar instance:
MyFoo foo = new MyFoo();
Bar bar = new Bar();
bar.other = foo.get();
This works perfectly. Type can be determined by java generics. No additional cast is needed.
However, if I want to use bar.setOther() then the type can not be determined:
MyFoo foo = new MyFoo();
Bar bar = new Bar();
bar.setOther(foo.get()); // Fails
Then I get the following compile error:
error: incompatible types: Object cannot be converted to Other
I don't understand why this doesn't work for the bar.setOther( foo.get() ) method but works when accessing the field directly bar.other = foo.get()
Any idea how to solve that without adding an extra cast like bar.setOther( (Other) foo.get() )
In an assignment, the Java compiler knows what return type to use for the method by looking at the type that it is being assigned to. So to answer your question
Any idea how to solve that without adding an extra cast like
bar.setOther( (Other) foo.get() ) ?
This is a way in which you can do that:
Other value = foo.get();
bar.setOther(value);
There is another way, which looks worse but still doesn't have a cast:
bar.setOther(foo.<Other>get());
And a third alternative: switch to Java 8; in Java 8 you can just do bar.setOther(foo.get());.
For Java 7, the rules for this type inference are specified in JLS section 15.12.2.8:
If any of the method's type arguments were not inferred from the types
of the actual arguments, they are now inferred as follows.
If the method result occurs in a context where it will be subject to
assignment conversion (ยง5.2) to a type S, then [...]
A method result is subject to assignment conversion if it is used in an assignment expression.
Otherwise, the unresolved type arguments are inferred by invoking the
procedure described in this section under the assumption that the
method result was assigned to a variable of type Object.
If the unresolved type argument is used in a method whose result is not used in an assignment expression, then the type argument is interpreted as if the method result was assigned to a variable of type Object. So this means in this case that if the result of the get() method is not assigned to a variable, then the return type of the get() method is taken to be Object.
Here's the code:
public class Main {
public static void main(String[] args) {
Gen<Integer> g = new Gen<Integer>(5);
System.out.println(g.getClass());
System.out.println(g.ob.getClass());
}
}
class Gen<T> {
T ob;
public Gen(T x) {
ob = x;
}
}
And here's the output
class Gen // This I understand
class java.lang.Integer // But if type erasure is happening, shouldn't this be java.lang.Object?
I get it that Type parameter T is erased at runtime, but then why is the type parameter of ob surviving at runtime?
Nope!
Consider this:
Object x = new Integer(1);
System.out.println(x.toString());
You'll get 1.
But shouldn't I get Object.toString()?
No. While x is a reference of type Object, the actual referent is an Integer, so at run-time, the Integer implementation of toString is called.
It is the same for getClass.
Type erasure is happening. Generics are a compile time type checking system. At run-time you still get the class (it is run-time type information). The linked Type erasure documentation says (in part)
Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Your instance has a type, it's Object. But an Object reference can refer to any sub-class (which is every class) in Java. You get the type it refers to.
No matter what type the variable has, the return value of getClass() depends on the variable's contents. So, since you basically have a variable Object ob which contains an Integer (your int was converted to it at the time you provided it as that constructor's parameter), the output of ob.getClass() is class java.lang.Integer.
Also, about your question as to why getClass() remembers the type argument: it doesn't. All it does is determine the content's class. For example:
class Foo {
public Foo() {}
}
class Bar extends Foo {
public Bar() {}
}
class Baz<T> {
public T object;
public Baz(T object) { this.object = object; }
}
If you now run the following snippet...
public static void main(String... args) {
Baz<Foo> obj = new Baz(new Bar());
System.out.println(obj.object.getClass());
}
You will notice that the output is not class Foo, it's class Bar.
Because when compiled, class Gen has an Object ob; The generics disappear from the final product. The angle brackets only play a role at compile-time, during static type checking. It's something the compiler can do for you to give you better peace of mind, to assure you that you're using collections and other paramterized types correctly.
the actual object assigned to ob at runtime is an instance of the Integer class, and ob.getClass() serves the purpose to find out the actual class of the object referenced by the pointer -> hence you will see java.lang.Integer printed.
Remember, what comes out is effectively class Gen { Object ob; ... }
Since my first exposure to generics was with C# , it took time get a hold of what type erasure is in java.
But after better understanding java generics , i realized that in my question i'm mixing 2 separate topics : Generics and Reflection.
The main question was , why did the second call here
System.out.println(g.getClass());
System.out.println(g.ob.getClass());
returned java.lang.Integer instead of java.lang.Object.
Looking at the docs for getClass() , the answer becomes obvious
Returns the runtime class of this Object.
so, getClass() doesn't return the type of reference but the actual object being referred to by the reference.
For ex :
Object o = "abc";
System.out.println(o.getClass());
The output wouldn't be the type of the reference java.lang.Object but rather the actual type of the object java.lang.String.
My idea is that there is a validator interface, which has method getRealValue(). The return value depends on field, it could be String, Integer or Long values.
My chances are:
I can do assign return type as Object and use casting every time after I called this method. (RuntimeError if wrong casting happened).
I can use generic an pass return type to validator when instantiate it (and I still have to use casting but inside method getRealValue and only once). Still RuntimeError if I will forget to pass return type or pass wrong type.
If there is a way I can store return type inside validator and use it?
For your 1st point, there is no way around getting a ClassCastException at runtime in case of an inappropriate cast.
In your second case you won't need to cast, see example here:
public interface Foo<T> {
public T getValue();
}
... then somewhere else:
public class Blah<T> implements Foo<T> {
#Override
public T getValue() {
// TODO write the code
// note that because of type erasure you won't know what type T is here
return null;
}
}
... then, somewhere else:
Blah blah1 = new Blah<String>();
String s = blah1.getValue();
Blah blah2 = new Blah<Long>();
// etc.
Finally, here's some literature for you:
Generics in Java
Inheritance in Java (has a section on casting)
I have this class, just for the purpose of learning:
public class MyClass{ //Looking for a solution without making my class also generic <Type>
//Private Arraylist var to hold the value called myvar
public MyClass(ArrayList<MyDesiredType> incoming) {
//CODE myVar=incoming
}
public MyDesiredType getType() {
return myVar.get(0);
}
}
Is there any way to infer in the incoming object from the constructor to the return type of the method without warnings and castings and loosing typesafeness, but most of all WITHOUT making the whole class GENERIC (seems redundant to me)? If not, why should I think this is not feasible for the compiler?
This is a reformulated question I already did, but it was my first one and I learned how to expose it clear because nobody understood. I tried to edit later the original question but everything was buried. I changed and simplified the example and try to put it easy. Original question: Java Generics Silly Thing (Why cant I infer the type?).
If there is any problem just tell it to me and I will remove it.
No, there is not. How would the compiler know what type to return? The generic type of ArrayList in the constructor will not be known during compile time. You either have to make the whole class generic or take another approach.
Consider this:
public class Test {
public static void main(String[] args) {
List<String> arrList = new ArrayList<String>();
arrList.add("FOO");
Test test = new Test(arrList);
String testStr = test.returnWhat();
System.out.println("testStr");
}
private final List myList; //warning
public <T> Test(List<T> ttype) {
myList = ttype;
}
public <T> T returnWhat() {
return (T) myList.get(0); //warning
}
}
This works but gives you warnings on the marked lines. So, really there is no way to achieve what you are describing without making the whole class generic.
Because, what if:
public class Test {
public static void main(String[] args) {
List<String> arrList = new ArrayList<String>();
arrList.add("FOO");
Test test = new Test(); // now what?
String testStr = test.returnWhat(0); // no warning...
JPanel p = test.returnWhat(0); // goes through without warning, real nice...
test.returnWhat(0); // returns Object
Test test2 = new Test(arrList);
test2.addElement(new Object()); // boom, inserted object into list of string.
String nono = test2.returnWhat(1); // the universe goes down. assign an object to string without warning. even
// though one COULD think the class is generic.
}
// private List<T> myList = new ArrayList<T>(); compiler error, T is unknown
private List myList = new ArrayList();
public Test() {
myList.add(new Object());
}
public <T> Test(List<T> ttype) {
myList = ttype;
}
public <T> T returnWhat(int index) {
return (T) myList.get(index);
}
public <T> void addElement(T el) {
myList.add(el);
}
}
The second one doesn't compile when myList is made generic. How could the compiler determine the type of <T> in the case where the default constructor is used?
Further, this could lead to serious problems with Objects in collections that rely on the fact that only certain types are inserted.
This will generate the following exception:
Exception in thread "main" java.lang.ClassCastException:
java.lang.Object cannot be cast to java.lang.String at
Test.main(Test.java:27)
Did I manage to convince you?
Real nice question, btw. I had to think about this one quite a bit.
When you say that you want the compiler to "infer in the incoming object from the constructor to the return type of the method without warnings and castings and loosing typesafeness", it seems that you are saying that it should infer the result of getType() from the input of the constructor. If both happen in the same function, it could. The problem is that the object may not exist in only one function, and so the extra type information (the generic type) is needed to pass this kind of object between functions.
For example, if I want to write a function that takes a MyClass object, I need to know what getType() will return so I can use the returned value. By adding a generic type of MyClass we are giving a description to what it holds.
Another way to look at it is that MyClass is a container. By adding generics, we are saying it is a container of a specific type of thing, and so we can more easily predict what we will get out of it.
There is no way for the compiler to know at runtime what type your arraylist is. I really dont see the problem using something along the lines of this:
public class MyClass<TYPE> {
private ArrayList<TYPE> incoming;
public MyClass(ArrayList<TYPE> incoming) {
this.incoming = incoming;
}
public TYPE getType() {
return incoming.get(0);
}
}
This way you can do:
ArrayList<Integer> numbers = createListOfNumbers();
MyClass<Integer> myClass = new MyClass<>(numbers);
Integer number = myClass.getType();
Or am i misinterpreting the question and you want to know the class at runtime?
No, if you want a class that can hold a list of a parameterized type.
Yes, if you want a class that can hold a list of exactly one type. You can declare that type explicitly in the field, constructor and accessor.
What you're forgetting is that not all code that you may run against is visible to the compiler! Jars can be added, removed, substituted at run time, that the compiler never saw. You may compile against an interface that is just:
public interface MyClassFactory {
MyClass getInstance();
}
Then at runtime you supply into the JVM an implementation. So the compiler never saw the actual code creating the MyClass that you will be using, so there is no way to perform such a compile time inference. You must either make the class generic or accept that there will not be type safety.