I want to use the class information that was captured by the setup of a generic method in Java to potentially create an instance. But I can't see how to reference the class without an actual instance. Is it possible?
public class Fancy {
static public <TypeToFind> TypeToFind createInSomeCase() {
// any type of logic with TypeToFind "class" generic will do, I just want to reference it.
// the below code is invalid, I could also declare a variable, but can't always create an instance to get the class
if (TypeToFind.getClass().equals(Something.class)) {
return TypeToFind.getInstance();
}
}
}
... so later on I could do:
TheResultIsTheParameter t = Fancy.createInSomeCase();
... instead of
TheResultIsAParameter t = Fancy.createInSomeCase(TheResultIsAParameter.class);
... or
TheResultIsAParameter t = Fancy.createInSomeCase(t);
Am I making this too complicated?
You can't do it, because generics are lost at runtime (due to type erasure). You have to pass a Class<?> parameter
Well, you require somethink that is logical, unfortunattelly generics in Java are only a syntactic sugar for reflection.
List<MyClass> list;
(...)
MyClass my = list.get(0);
will compile to
MyClass my = (MyClass) list.get(0);
and this is what will you see in bytecode.
What is more, using reflection or casting to untyped list you can put any object into list and in both codes you'll get ClassCastException.
So the generics exists only on compiler level. A big feature which adds nothing new, only shortens a code in most cases.
As long as you do not try and statically (at compile time) reference any particular class, nothing prevents you from doing something like this:
public class GenericsTest {
#Test
public void testMe() {
GenericsTest test = new GenericsTest();
System.out.println(test.get("Hello").getClass());
}
public GenericsTest() {
super();
}
public <T extends Object> T get(T entity) {
return newInstanceForClass((Class<T>)entity.getClass());
}
public <T extends Object> T newInstanceForClass(Class<T> clazz) {
try {
return clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
But as you can see, you need to pass in an object of the class you are trying to instantiate, which might not be want you are after. In which case the only other option is to pass in a Class parameterized with the generic type, for reasons that other posters have eloquently stated.
Related
I am a beginner in Java.
How do I initialize a list of a class type received as a parameter? I have a method that takes in a Class<?> argument as a parameter, and I want to create a list of this class type.
public List<?> returnList(Class<?> listItemType) {
//create a list of type "listItemType"
//populate the list
//return the list
}
I have tried doing
List<listItemType> list = new ArrayList<listItemType>()
but VSCode shows listItemType cannot be resolved to a type.
I have also tried <? extends listItemType>, <listItemType.getName()> and the below code, but they don't seem to work either.
Object obj = listItemType.getDeclaredConstructor().newInstance();
List<obj.getClass()> = new ArrayList<obj.getClass()>();
If you make the method a generic method with a named type parameter it will work. For example:
public <T> List<T> returnList(Class<T> listItemType) {
List<T> list = new ArrayList<>();
try {
list.add(listItemType.getConstructor().newInstance());
} catch (ReflectiveOperationException ex) {
// do something
}
return list;
}
Note that various exceptions are possible. For example, the class may not have a suitable constructor, constructor may not be accessible, or it may throw exceptions. A complete version of this method should do something appropriate, or declare the relevant exceptions so that they can propagate to the caller and beyond.
Without the named type parameter T you don't connect the wildcard in List<?> to the wildcard in Class<?>. So the compiler wouldn't know that returnList(Foobar.class) is intended to return a List<Foobar>.
And there is no way that you can use the variable name listItemType as a class name as in List<listItemType>. That's why you were getting "listItemType cannot be resolved to a type". Variable names and type names are in different Java namespaces.
Your attempt <? extends listItemType> fails for the same reason, and
<listItemType.getName()> fails because an expression is not a type.
Another way to think about those failed attempts is that they use runtime values (e.g. the value of listItemType) to do compile time type checking. That implies that the compiler needs to be able to look into the future to see what the runtime values are going to be. Clairvoyance is not a feature of current generation Java compilers :-).
What you need is a generic method.
Generic methods allow type parameters to be used to express
dependencies among the types of one or more arguments to a method
and/or its return type.
This is a full example:
public class NewMain {
private static class Foo {
public Foo() {
}
}
public static void main(String[] args) {
// TODO code application logic here
List<Foo> foos = returnList(Foo.class);
System.out.println(foos.get(0));
}
public static <T> List<T> returnList(Class<T> listItemType) {
List<T> list = new ArrayList<>();
try {
T obj = listItemType.getConstructor().newInstance();
list.add(obj);
} catch (Exception ex) {
Logger.getLogger(NewMain.class.getName()).log(Level.SEVERE, null, ex);
}
return list;
}
}
public class ResourceAssembler<T extends BasedEntity> {
public Resource<T> toResource(T entity) {
ExtendsBasedEntity e = getExtendsBasedEntity();
toResource(e); //<----compile error
//some other code
}
}
public class ExtendsBasedEntity extends BasedEntity{}
But if you call it from the outside its fine
//some other class
new ResourceAssembler<ExtendsBasedEntity>().toResource(new ExtendsBasedEntity())
Why?
Error:(28, 25) java: incompatible types: spring.BasedEntity cannot be converted to T
T may not be ExtendsBasedEntity, but some other subtype of BaseEntity, hence the compile error.
One way to "fix" the problem, is to use a type token
public class ResourceAssembler<T extends BasedEntity> {
private final Class<T> type;
public ResourceAssembler(Class<T> type) {
this.type = type;
}
public Resource<T> toResource(T entity) {
toResource(type.newInstance());
//some other code
}
}
Assuming that works for you.
Let's create two classes extending BasedEntity and call them EBE1 and EBE2.
Now you create a ResourceAssembly object using EBE1 as the type parameter. But let's say that in the implementation of the toResource method you do something like return toResource(new EBE2());.
So the return type of toResource() is becoming Resource<EBE2> but that is wrong because according to the structure you should return Resource<EBE1>. And that's why the compile time error. And type safety instincts of Java kicks in.
If you want to do return a generic for the toResource method then you either have to pass in the entity object down as it is or change it to the concrete type that you are initializing it within and not use generic (although I don't know why would anyone use the second option, but it's a "solution" to make it "compile").
Also, on the outside when you declare it. You are not specifying the type parameter for ResourceAssembly and hence it's a raw one. Try to do it with a type param. You will have red squiggly lines there as well.
Here is an example:
static class Resource<T> {
}
static class BasedEntity {
}
static class ExtendsBasedEntity1 extends BasedEntity {
}
static class ExtendsBasedEntity2 extends BasedEntity {
}
static public class ResourceAssembler<T extends BasedEntity> {
public Resource<T> toResource(T entity) {
return toResource(new ExtendsBasedEntity1()); //<----compile error
}
}
public static void main(String[] args) {
new ResourceAssembler<ExtendsBasedEntity1>().toResource(new ExtendsBasedEntity1()); // <---- No errors or warnings. This is valid and legal
new ResourceAssembler<ExtendsBasedEntity2>().toResource(new ExtendsBasedEntity1()); // <----- red squiggly lines here
new ResourceAssembler().toResource(new ExtendsBasedEntity2()); // <--compiler warning about raw types but no error
}
If you anyhow need to make it work the way you want it to, then instead of returning Resource<T>, return Resource<ExtendsBasedEntity> because you are recursing inside a generic method and looks like you need an object of concrete type to go in as the parameter for the recursive call. So it would make sense to do so.
Or else, go with #Bohemian's approach and make sure that in the class declaration of the type that you are using, there is a no-args constructor or else you will be having InstantiationException.
Problem:
I want to create a generic function which will return me strogly typed object.
Function :
public <T > T GetPet(AnimalKingdom allAnimals,int id) {
return (T) allAnimals.getAnimalsManager().findAnimalById(id);
}
The above function will return a strongly typed object or throw error.
Usage :
GetPet<Tiger>(thisZoo,tigersId).Roar();
Coming from C# background. Googled for the same but was not able to find a solution, it seems that I need to pass the generic type in function for it to work.
How can the above scenario implemented in java.
In Java, you explicitly specify the type arguments of the generic method before the method name. For example:
class Example {
public static <T> T getPet(AnimalKingdom allAnimals, int id) {
return (T)allAnimals.getAnimalsManager().findAnimalById(id);
}
}
Then to invoke:
Example.<Tiger>getPet(thisZoo, tigersId).roar();
Alternatively, you can use type inferencing:
Tiger tiger = Example.getPet(thisZoo, tigersId);
tiger.roar();
By the way, your getPet method is not very safe because there is no runtime check performed to ensure the object returned is actually an instance of Tiger. In fact, the Java compiler gives a warning on this line:
return (T)allAnimals.getAnimalsManager().findAnimalById(id);
The reason is because the cast to (T) is unchecked due to type erasure.
To strengthen your code, I suggest the following change:
class Example {
public static <T> T getPet(Class<T> clazz, AnimalKingdom allAnimals, int id) {
return clazz.cast(allAnimals.getAnimalsManager().findAnimalById(id));
}
}
Then to invoke:
Example.getPet(Tiger.class, thisZoo, tigersId).roar();
The benefit of passing in the class object (Tiger.class) is that:
It provides the compiler the type to use for <T> (again via type inferencing).
You can add your own explicit runtime type check by calling the Class.cast method. The Java compiler warning goes away, and type safety is restored. For example, if findAnimalById mistakenly returns an instance of Bear when you expected Tiger, you will get a ClassCastException.
please try something like
Tiger tiger = GetPet(thisZoo,tigersId);
tiger.Roar();
Using a generic is simply the wrong choice here. You should take a look at inheritance, virtual function, abtract classes and interface.
As SylvainL said, generics is not the right choice here. This looks like a simple inheritance / interface problem to me.
New getPet method:
public Animal getPet(AnimalKingdom allAnimals, int id)
{
return allAnimals.getAnimalsManager().findAnimalById(id);
}
Using inheritance:
class Animal { void Roar() { throw new Exception("This animal cannot roar!"); } };
class Tiger extends Animal { #Override void Roar() { ... }; ... };
Usage:
getPet(thisZoo, tigersId).Roar();
OR Using an interface: (a little more complicated but probably generally better)
Note: you don't really need Animal in this case, you can just use Object
class Animal { ... }; // no Roar method
interface Roarer { void Roar(); };
class Tiger extends Animal implements Roarer { #Override void Roar() { ... }; ... };
Note: You can stick either of the below usages into a method to just end up with something like:
roar(getPet(thisZoo, tigersId));
Usage: (you can remove the try {} catch, but this is generally not a good idea)
try
{
((Roarer)getPet(thisZoo, tigersId)).Roar();
}
catch (ClassCastException e) { /* handle error */ };
Usage alternative: (to safely avoid try {} catch, haven't tried this yet, but I think it might work)
Animal animal = getPet(thisZoo, tigersId);
if (Roarer.class.isAssignableFrom(animal.class))
((Roarer)animal).Roar();
else
// handle error
I have a class with a generic type and I want to get the class of the generic type.
I found a solution with the following code but when I use ProGuard for obfuscation, my app stops working.
Is there an other way of doing this?
public class comImplement<T> {
private T _impl = null;
public comImplement() {}
public T getImplement() {
if (_impl == null) {
ParameterizedType superClass =
(ParameterizedType) getClass().getGenericSuperclass();
Class<T> type = (Class<T>) superClass.getActualTypeArguments()[0];
try {
_impl = type.newInstance();
} catch (Exception e) {
}
}
return _impl;
}
}
You can not get the type of superclass unless it's parametrized with another generic class, e.g - List or something like that. Due to reification type parameters will be lost after compilation. You may want to solve the problem with passing instance of class you're about to create, to method "getImplement, like below:
public T getImplement(Class<T> clz) {
// do your initialization there
}
another problem which might raise with your code - is race condition in case if the object is shared between several threads.
My goal is to develop a class that can output an object of a specified class.
public class GetMe<T> {
public T get() {
Object obj = generateObject();
return (T) obj;
}
}
Now, I know this isn't possible due to erasure. So, we can pass in a class instance and use that to cast.
public class GetMe<T> {
public GetMe<T>(Class<T> clazz) {
this.clazz = clazz;
}
public T get() {
Object obj = generateObject();
return clazz.cast(obj);
}
}
This works great! As long as the class isn't parameterized. If it is, then I've got a problem.
I'm not allowed to use List<String>.class. If I pass in a ParameterizedType (which in itself is difficult to generate), there's no cast method to use.
Is there a way out of this quagmire?
I think super type tokens may solve this problem for you.
The problem with List<String> is that, because of erasure, it would at runtime indistinguishable from any other List<?>. The easiest way around this is to create a new class or interface which has the generic part "fixed", like
public interface StringList extends List<String> {
/* nothing to see here */
}
This way you have a type token (the StringList.class object) which you can pass around at runtime and specifies exactly what you want, but without the need for generics at runtime.
Here is just a small idea. I'm not really sure if it will fit in your context but nevertheless:
public class GetMe<T>
{
public List<T> getList() {
#SuppressWarnings("unchecked")
List<T> result = (List<T>) new LinkedList();
return result;
}
}
Cheers!
The first problem is how you plan to instantiate a List object. If you disclose more of what you are trying to build, we may be able to help you better.
You may want to use Type instead of Class. Type can represent all generic types, although it's not pleasant to work with.
abstract public class GetMe<T>
{
Type type;
public GetMe<T>(Type type)
{
this.type = type;
}
}
Another problem is how to create a generic type like List<String>. The "super type token" looks neat in syntax, in reality it's basically
static class XX extends TypeReference<List<String>>{}
....
Type typeListString = Util.extract(XX.class);
I would much prefer this way
List<String> f;
Type typeListString = getDeclaredField("f").getGenericType();
Actually, many of these frameworks that do fancy runtime generic magics are working on instance fields only.
I think the confusion comes from the fact that you're trying to create an object from List<> which in face it an interface, not an object.
So no matter what you'd try, you just can't create an instance of List<> , (interfaces aren't actual classes, and don't have constructors)
Try using a constraint to avoid having interfaces put in the declaration:
public class GetMe<T extends Object>
This will guarantee that T is an actual class and not an interface.