Cannot access class methods via generics - java

I changed my method to generic method. What is happening now is that I was deserializing the class inside the methodB and accessing its methods which I can not do anymore.
<T> void methodB(Class<T> clazz) {
T var;
HashMap<String, T> hash = new HashMap<>();
}
void methodA () {
methodB(classA.class);
}
Initially inside methodB with no generics,
var = mapper.convertValue(iter.next(), ClassA.class);
var.blah() //works fine
After using generics,
var = mapper.convertValue(iter.next(), clazz);
var.blah() //cannot resolve the method.
How do I access blah() method of classA?

I think you should use interfaces instead of generics, if you want to call the same 'blah' function on a variety of classes (A,X,Y,Z) (each of which has the same function signature)..
Your other option (if you cannot modify A, e.t.c) is to use reflection. read more about this in https://docs.oracle.com/javase/tutorial/reflect/

Thanks to Passing a class with type parameter as type parameter for generic method in Java. Solved using TypeToken

The line where you assign to var at runtime is absolutely irrelevant. The only thing that matters for compiling a call is the static (compile-time) type of var. Since T is unbounded (other than by Object), it is not known to support any methods, other than those provided by Object. Both pieces of code should fail to compile.

Related

Can you explain why the first unwrapped method reference does not compile?

In this example, passing a method reference to Stream.of does not work but once it's wrapped it works. I can't quite get my head around why this is. Isn't the method reference equivalent to the functional interface?
public class A {
String a() {
return "";
}
void b() {
Stream.of(this::a); // won't compile
Stream.of(wrap(this::a)); // will compile
}
static <T> Supplier<T> wrap(Supplier<T> f) {
return f;
}
}
Stream.of has the following signature:
public static<T> Stream<T> of(T t)
The following example will compile because you explicitly providing a type information for T.
Stream<Supplier<String>> a = Stream.of(this::a);
The first example Stream.of(this::a); equivalent to:
Object a = this::a;
where Object is not a functional interface and will not compile.
Providing with a functional interface this example compiles:
Runnable a = this::a;
Stream.of(a);
In the second example, wrap provides a functional interface Supplier
Stream.of(T) expects an Object and you pass to it a method reference in the first statement. But an Object parameter is not a functional interface, so it cannot accept a method reference or a lambda that is not specifically typed.
With lambda, it would produce also an error : Stream.of(()->this.a()).
A simpler example could be Stream.of(()-> "foo") that will just not compile.
But if you type the method reference or the lambda it works :
Stream.of((Supplier<String>) this::a)
or
Stream.of((Supplier<String>) () -> this.a())
In the working statement you pass to Stream.of(T) a parameter that is a Supplier<String>. That refers to a functional interface but that is typed as in the previous working examples, so it is valid as parameter that expects an Object.
this::a is contextless and could mean different things. You need to provide some context to help the compiler to figure out what you actually meant by this::a.
Stream.<Supplier<String>>of(this::a);
Though, that Stream<Supplier<String>> doesn't seem to be what you wanted. If you need a Stream<String>, use Stream.generate: no extra type information needed since the method takes a Supplier<T> (no ambiguity here).
Stream.generate(this::a);
On a side note, both statements expect you to save their results into variables. Defining variables of the right type often facilitates resolving such issues.
Stream<Supplier<String>> s1 = Stream.of(this::a);
Stream<String> s2 = Stream.generate(this::a);
All credit to #J-Alex and #Holger for their precious comments.

Java generic method. Why is T deduced to be Map?

Consider the following code
class MyClass {
public MyClass(Map<String, String> m) {
System.out.println("map");
}
public MyClass(SortedMap<String, String> m) {
System.out.println("sortedmap");
}
}
public class Test {
public <T extends Map<String,String>> Test(T t) {
new MyClass(t);
}
public static void main(String[] args) {
new Test(new TreeMap<String,String>());
}
}
It prints map. Why is T deduced to be Map instead of SortedMap in public <T extends Map<String, String>> Test(T t) ? Is there a way to change this behaviour in order to use the most concrete constructor for MyClass?
The resolving which constructor of MyClass is called is done at compile time. When the compiler compiles the code of the Test constructor, it does not know what T actually is, it just knows that it is guaranteed to be a Map<String, String>, so it cannot do anything else than binding the constructor call to the constructor that takes a Map.
The knowledge that in your code T is a TreeMap is only present within the body of the method main, not outside. Consider for example what would happen if you added a second caller of the Test constructor that actually passes a HashMap.
Java generics work such that the code of a generic method is only compiled once for all possible generic parameter values (and only present once in the byte code), there is not like in other languages a copy of the generic method for each generic type.
In general, it is not possible in Java to let a single method/constructor call in the code actually call different methods/constructors at runtime depending on the type of arguments. This is only possible for method calls depending on the runtime type of the called object (dynamic binding with overwritten methods).
Overloading (what you have here) works only at compile time by looking at the static type of the arguments.
The typical solution for this situation would be to use an instanceof SortedMap check within the constructor of MyClass.
Another possible (more elegant) solution is the visitor pattern, but this works only with classes that are prepared for it (so not with Map instances if you do not wrap them within a class of your own).

When is it acceptable to pass a Class<T> argument to a generic method?

Methods that are generic using the T parameter can for sure be handy. However, I am curious what the use of a generic method would be if you pass an argument such as Class<T> clazz to the method. I've come up with a case that maybe could be an possible use. Perhaps you only want to run a part of the method based on the type of class. For example:
/** load(File, Collection<T>, Class<T>)
* Creates an object T from an xml. It also prints the contents of the collection if T is a House object.
* #return T
* Throws Exception
*/
private static <T> T void load(File xml, Collection<T> t, Class<T> clazz) throws Exception{
T type = (T) Jaxb.unmarshalFile(xml.getAbsolutePath(), clazz); // This method accepts a class argument. Is there an alternative to passing the class here without "clazz"? How can I put "T" in replace of "clazz" here?
if (clazz == House.class) {
System.out.println(t.toString());
} else {
t.clear();
}
return T;
}
Is this an accepted practice? When is the Class<T> clazz argument useful with generic methods?
Is this an accepted practice?
Well, to me.. no not really. To me, it seems somewhat pointless when you can simply define some boundaries on the type of T. For example:
private static <T extends House> void load(Collection<T> t)
This will guarantee that either the object is of type House or of a subclass of House, but then again if you only want an instance of type House or it's subclasses, it should really just be:
private static void load(Collection<House> houses)
The idea of generics is to make a method or a class more malleable and extensible, so to me it seems counter-intuitive to start comparing class types in the method body, when the very notion of generics is to abstract away from such details.
I'd only pass class objects if the generic type could not be derived otherwise. In your case, the compiler should be able to infer T from the collection. To treat specific objects differently, I'd use polymorphism - e.g. House#something() and Other#something(), and just call anyObject.something().
I think it is acceptable but if it can be avoided then you should. Typically, if you can have different methods which accepts different type, then do it instead of one method which uses if clauses to do something different depending on the type of the parameter. You could also delegates to the class the operation you want to make specific for a given type.
In your case, you could simply test the type of each element of the collection using instanceof, to do what you need for the specific type. But it won't work if the list is empty.
A typical use is if you need to get the type to create it and you can find it from another way. For instance, Spring uses it to load a bean from its name:
<T> T getBean(Class<T> requiredType)
In that case, it cannot be avoided (without having to cast).
If the returned value or other parameters types are dependent or need to be equal, generics will add compile time checks, so that there's no need to cast to T.
Examples
<T> T createNewInstanceOfType(Class<T> type);
<T> void addValueToCollection(Collection<T> collection,T value);
<T> List<Class<? extends T>> findSubClassesInClasspath(Class<T> superType);
Raw types
It is still possible to defer a casting error until runtime (ClassCastException) with some casts, e.g. with implicit casts from non-generic (raw) types to generic ones:
List nonGenericList = new ArrayList();
nonGenericList.add(new Integer(42));
List<String> wreckedList = nonGenericList;
The compiler will generate a bunch of warnings, unless you suppress them with annotations or compiler settings.
Compiler Settings (Eclipse):
For example, the usage of raw types generates a warning per default, one can treat warnings as errors and even as fatal errors:
You would pass a Class<T> argument in generics if, and only if, you would pass a Class argument before generics. In other words, only if the Class object is used in some way. Generics serves as a compile-time type checking tool. However, what arguments you pass should be determined by the runtime logic of the program, and should be irrelevant of generics.
I haven't seen passing a Class object in order to check the runtime type of an object as a common use case for generics. If you're doing that, there's a good chance that there's a better way to set up your class structure.
What I have seen is if you need to create a new instance of the class in question, or otherwise use reflection. In that case you do have to pass the Class object, because Java cannot derive it at runtime thanks to type erasure.
In your case actually having the Generic parameter is not strictly needed.
Since the output of the function you are describing does not depend on the type of the input you might as well use wild cards.
private static void stuff(Collection<?> t){
Object next = t.iterator().next(); //this is ugly and inefficient though
if(next instanceof House){
System.out.print(next.toString());
}else{
t.clear();
}
}
The only time you should use generic parameter is when the type of the result of a function will be dependent of the type of the parameters.
You will need to pass the Class corresponding to the type when your code will need it; most of the time this happens when:
- You need to cast/type check objects to T
- There is serialization/deserialization involved.
- You cannot access any instance of T in your function and you cannot call the getClass() method when you need it.
Passing a Class on every generic function will result in you passing an unnecessary parameter most of the time, which is regarded as bad practice.
I answered a similar discussion in the past:
When to use generic methods and when to use wild-card?

Java Generics methods - using for the first time

I am trying to use a generic method for the first time and am somewhat confused. I created a simple example to demonstrate that I am probably going about this the wrong way and need straightening out. I am using Eclipse 3.6.1. I was under the impression that the compiler determined the argument types through inference, but am not sure why it is forcing me use casting in the generic method. This is a simple example.
class Test1
{
Test1 () {};
public String getX () {return "Test1"};
};
class Test2
{
Test2 () {};
public String getX () {return "Test2"};
};
my main method:
public static void main(String args[])
{
Test1 tst1 = new Test1();
Test2 tst2 = new Test2();
System.out.println("result: " + displayTest(tst1, tst2));
}
static <T,S> boolean displayTest(T x, S y)
{
System.out.println("X: " + ((Test1) x).getX());
System.out.println("Y: " + ((Test2) y).getX());
if (((Test1) x).getX().equals(((Test2) y).getX()))
return true;
else
return false;
}
I thought the compiler would know that T in this case was an instance of Test1 and S was Test2, yet in Eclipse, getX is not a valid method. In order to get this to compile, it forces me to cast the objects to the correct type, which seems to me to be against the general principles of a generic method.
Obviously, I am not getting this and am doing something wrong. How does the compiler know what the types are in the generic method then ? How should something like this be done ? In my large system where I am trying to implement this, I have several methods that operate on different types of objects and am trying to make them generic. i.e. Method 1 calls Method 2 (which uses the generic type), which in turn calls Method 3 (again passing the generic types). I was hoping only the start of the function calls (calling of method 1 in this case) needed to know what type the objects were and all subsequent methods were just generic methods.
Many thanks.
The compiler does not know the types inside the method. They can be anything.
If you use <T extends Y>, where Y is an interface defining getY(), it would work.
The point of generics is to ensure compile-time safety. They are mainly a compile-time notion. For example:
public static <T> T instantiate(Class<T> clazz) throws Exception {
return clazz.newInstance();
}
This method can be used, without any cases, like this:
Foo foo = instantiate(Foo.class);
Bar bar = instantiate(Bar.class);
Another example, from the Collections framework. There is Collections.enumeration(collection), which is generic. So:
Enumeration<String> enumeration1 =
Collections.enumeration(new ArrayList<String>(..));
Enumeration<Integer> enumeration2 =
Collections.enumeration(new ArrayList<Integer>(..));
No casts, but you are certain that these will be the types. And then the nextElement() method will return either String or Integer, without the need to cast:
String s = enumeration1.nextElement();
Integer i = enumeration2.nextElement();
Without generics, you would need to use casts in these examples, and if you have passed the wrong argument, you would get a runtime exception (most likely a ClassCastException). With generics you get the exception at compile-time.
The compiler actually adds these casts on your behalf, but only after it is certain that the cast can't go wrong.
The problem is that at compile time the compiler does not know the concrete type of T and S. Your main method calls displayTest with concrete instances of Test1 and Test2, but there's nothing stopping you from calling it with, say, a String and an Integer.
This means that you can't call getX on x because you can't guarantee that T is a subclass of Test1.
You can constrain the type of T and S with captures:
boolean <T extends Test1, S extends Test2> displayTest(T x, S y)
This tells the compiler that T must be a Test1 (or a subclass of Test1) which means you don't need to cast.
Why would the compiler know that T and S are Test1 and Test2? You can call displayTest from anywhere in your code, any number of times with any number of different object types. What is it supposed to do there?
Also, eclipse != compiler. Eclipse's code completion doesn't "compile" anything. The compiler knows exactly what types are being put in there, but only for each CALL to that function. That's why it's called generic. It can take any generic type.
When you say the compiler determines the argument types through inference, it means something different than what you are thinking.
Since displayMethod is a generic method, the "proper" way to call the method is to tell the compiler what T and S stand for in this particular method call by calling it as
ClassName.<Test1,Test2>displayMethod(tst1,tst2)
But since java is smart, the compiler can infer w/o you telling it exactly what T and S, that T and S are going to be Test1 and Test2 if you just do,
ClassName.displayMethod(tst1, tst2)
Also, like cameron skinner said, you can fix your code by making T extend Test1 and S extend Test2. Another fix that you can do is leave <T, S> as it is, and renaming your getX() method to the override the toString() method. Since toString() is a member of the Object class, the generic classes T and S will have access to it and you won't need to restrict your method to only accept arguments which are of type Test1 and Test2

Method has the same erasure as another method in type

Why is it not legal to have the following two methods in the same class?
class Test{
void add(Set<Integer> ii){}
void add(Set<String> ss){}
}
I get the compilation error
Method add(Set) has the same erasure add(Set) as another method in type Test.
while I can work around it, I was wondering why javac doesn't like this.
I can see that in many cases, the logic of those two methods would be very similar and could be replaced by a single
public void add(Set<?> set){}
method, but this is not always the case.
This is extra annoying if you want to have two constructors that takes those arguments because then you can't just change the name of one of the constructors.
This rule is intended to avoid conflicts in legacy code that still uses raw types.
Here's an illustration of why this was not allowed, drawn from the JLS. Suppose, before generics were introduced to Java, I wrote some code like this:
class CollectionConverter {
List toList(Collection c) {...}
}
You extend my class, like this:
class Overrider extends CollectionConverter{
List toList(Collection c) {...}
}
After the introduction of generics, I decided to update my library.
class CollectionConverter {
<T> List<T> toList(Collection<T> c) {...}
}
You aren't ready to make any updates, so you leave your Overrider class alone. In order to correctly override the toList() method, the language designers decided that a raw type was "override-equivalent" to any generified type. This means that although your method signature is no longer formally equal to my superclass' signature, your method still overrides.
Now, time passes and you decide you are ready to update your class. But you screw up a little, and instead of editing the existing, raw toList() method, you add a new method like this:
class Overrider extends CollectionConverter {
#Override
List toList(Collection c) {...}
#Override
<T> List<T> toList(Collection<T> c) {...}
}
Because of the override equivalence of raw types, both methods are in a valid form to override the toList(Collection<T>) method. But of course, the compiler needs to resolve a single method. To eliminate this ambiguity, classes are not allowed to have multiple methods that are override-equivalent—that is, multiple methods with the same parameter types after erasure.
The key is that this is a language rule designed to maintain compatibility with old code using raw types. It is not a limitation required by the erasure of type parameters; because method resolution occurs at compile-time, adding generic types to the method identifier would have been sufficient.
Java generics uses type erasure. The bit in the angle brackets (<Integer> and <String>) gets removed, so you'd end up with two methods that have an identical signature (the add(Set) you see in the error). That's not allowed because the runtime wouldn't know which to use for each case.
If Java ever gets reified generics, then you could do this, but that's probably unlikely now.
This is because Java Generics are implemented with Type Erasure.
Your methods would be translated, at compile time, to something like:
Method resolution occurs at compile time and doesn't consider type parameters. (see erickson's answer)
void add(Set ii);
void add(Set ss);
Both methods have the same signature without the type parameters, hence the error.
The problem is that Set<Integer> and Set<String> are actually treated as a Set from the JVM. Selecting a type for the Set (String or Integer in your case) is only syntactic sugar used by the compiler. The JVM can't distinguish between Set<String> and Set<Integer>.
Define a single Method without type like void add(Set ii){}
You can mention the type while calling the method based on your choice. It will work for any type of set.
It could be possible that the compiler translates Set(Integer) to Set(Object) in java byte code. If this is the case, Set(Integer) would be used only at compile phase for syntax checking.
I bumped into this when tried to write something like:
Continuable<T> callAsync(Callable<T> code) {....}
and
Continuable<Continuable<T>> callAsync(Callable<Continuable<T>> veryAsyncCode) {...}
They become for compiler the 2 definitions of
Continuable<> callAsync(Callable<> veryAsyncCode) {...}
The type erasure literally means erasing of type arguments information from generics.
This is VERY annoying, but this is a limitation that will be with Java for while.
For constructors case not much can be done, 2 new subclasses specialized with different parameters in constructor for example.
Or use initialization methods instead... (virtual constructors?) with different names...
for similar operation methods renaming would help, like
class Test{
void addIntegers(Set<Integer> ii){}
void addStrings(Set<String> ss){}
}
Or with some more descriptive names, self-documenting for oyu cases, like addNames and addIndexes or such.
In this case can use this structure:
class Test{
void add(Integer ... ii){}
void add(String ... ss){}
}
and inside methods can create target collections
void add(Integer ... values){
this.values = Arrays.asList(values);
}

Categories

Resources