How to pass a parameterized class as an argument - java

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.

Related

Lambda generic type bounds

I have the following method:
public <T> T getObjectFromMessage(Class<T> clazz) {
return gson.fromJson(compressor.decompress(message.getJsonInputs(s3)), clazz);
}
I want to pass getObjectFromMessage as a parameter into a lambda that's supplied to me. The lambda can then supply the class of the object that it expects to find in the message, and get an instance of it back. Is there a way to do this without losing the type information?
I can force it to work with casting and some Object bounds, but I'd really like the consumer to know that if it passes in a Class<T> it will get a T back, much like any method with generic bounds.
In the consuming lambda, I'm currently forced to do declaration gymnastics like:
public void consume(Function<Class<Object>, Object> getInputs){
MyType type = (MyType)getInputs.apply(MyType.class);
}
but there are cases where I want to try to parse the inputs, and if I fail, try a different class. The generics really need to be inferred per-call, as a method would.
You can define a custom function interface with a generic method:
interface TypeFunction {
<T> T apply(Class<T> clazz);
}
public void consume(TypeFunction getInputs) {
MyType type = getInputs.apply(MyType.class);
}
Some thoughts on this... does a function that invokes another function add anything?
For example:
<T> T consume(ReadType<T> typeFunction) {
T type = typeFunction.read();
return type;
}
There are generic types being managed in at least 3 entities here:
The class that contains the message string.
The function that deserializes the message string to an object.
The function that calls the deserializer function.
I expect from the question that the Object that holds the message string is also responsible for deserialization? If so you could consider declaring the generic type there. That would prevent the need of passing the type to the deserializer function, for example this could be simplified further:
<T> ReadType<T> readObjectFromMessage(Class<T> clazz) {
return () -> readValue(clazz);
}
I've declared ReadType as:
interface ReadType<T> {
T read();
}
Also implemented a simple test to check outputs and visualise how this might be used:
#Test
public void consumeTypeTest() throws Exception {
String message = "{\"foo\":\"hello\",\"bar\":\"world\"}";
GenericFunctions genericFunctions = new GenericFunctions(message);
ReadType<MyType> myTypeFromMessage = genericFunctions.readObjectFromMessage(MyType.class);
MyType myType = genericFunctions.consume(myTypeFromMessage);
Assert.assertThat(myType, equalTo(new MyType().setFoo("hello").setBar("world")));
}

Java List<Parent> and List<Children> method

Suppose we have the following:
public class Parent{
public void setXXX(String XXX);
public String getXXX();
}
public class Children extends Parent{
....
}
Now I want to create a method called clone List like the following:
public List<Something> cloneList(List<Something> original){
List<Something> newList=new ArrayList<>();
for(Something e:original){
Something newE=new Something();
newE.setXXX(e.getXXX());
newList.add(newE);
}
return newList;
}
The thing is we want cloneList can be applied to both List<Parent> and List<Children>, so is there anyway that applicable for "Something"?
Something cannot be "? extends Parent" or "Parent" due to the Java Collection<Parent> incompatible with Collection<Children>
Assumption:
1. Don't want to use any serialization approach or reflection.
We are unable to modify the Parent and Children class. This is predefined in 3rd party Jar.
SuperParent class is not possible because we cannot modify Parent as stated in 2.
That is not possible in Java. Take a look at Generic syntax for extends or equal to.
You could change your method as follows and make your Parent class extend SuperParent.
public static <T extends SuperParent> List<T> cloneList(List<T> original, Class<T> type) throws IllegalAccessException, InstantiationException {
List<T> newList=new ArrayList<>();
for(T e : original){
T x = type.newInstance();
x.setXXX(e.getXXX());
newList.add(x);
}
return newList;
}
Also, you could choose another cloning approach. For example, using Apache Commons' SerializationUtils:
List<Children> result = (List<Children>) SerializationUtils.clone(originalList);
You cannot use generics this way, only reflection.
For a type variable T, you cannot use new T(). That's because generics are a compile-time mechanism, and new is used in run-time to create a specific-type object, and the compiler cannot create the appropriate reference to the type at compile time. So while this:
new ArrayList<T>();
is legal, because the compiler actually compiles it into the code for creating the raw ArrayList type, this:
new T();
is not, because the compiler does not even know what the actual class will be (even if it was just defined as T extends Parents it could be a class that has not even been written when the program compiled, like Grandchildren or something), and does not even know if it has a parameterless constructor.
In a general sense, you should be able to use a method having this signature:
public <T extends Parent> List<T> cloneList(List<T> original)
That's not your biggest problem, however. THAT would be obtaining copies of the list elements. Your code cannot use
T newE = new T(); // doesn't work
because the existence of a nullary constructor for type argument T cannot be guaranteed. Instead, you need a method that will return a correctly-typed copy. You cannot do this with complete type safety, but you can come close. You can implement these methods:
public Parent Parent.copy();
public Children Children.copy();
... in whatever way is appropriate, and then write your method like so:
public <T extends Parent> List<T> cloneList(List<T> original) {
List<T> newList = new ArrayList<>();
for (T originalItem : original) {
newList.add(original.getClass().cast(original.copy()));
}
return newList;
}
(Note that although the documented return type of Object.getClass() is Class<?>, which would not work for this purpose, the method documentation says that the return type is actually a bit more specific than that, enough so to make this work.)
Change the signature of cloneList to:
public <X extends Parent> List<X> cloneList(final List<X> original)
Then it will work, at least for the method signature. You can internally construct a List<Parent> and then cast it to List<X> and ignore the warnings if you need to; there's no way to find out the runtime type of "X".

method not applicable for the arguments, but not sure why

I have the following method which takes a list of classes as a parameter:
public List<Interface> getInterfacesOfTypes(List<Class<? extends InternalRadio>> types) {
List<Interface> interfaces = new ArrayList<Interface>();
for(Interface iface : _nodes)
if(types.contains(iface._type))
interfaces.add(iface);
return interfaces;
}
What I want to do is create a wrapper for it where only a single class is specified, which calls the above method with a list of only that one class:
public List<Interface> getInterfacesOfType(Class<? extends InternalRadio> type) {
return getInterfacesOfTypes(Arrays.asList(type));
}
However, I am getting an error:
The method getInterfacesOfTypes(List<Class<? extends InternalRadio>>) in the type InterfaceConnectivityGraph is not applicable for the arguments (List<Class<capture#3-of ? extends InternalRadio>>)
I can't figure out why this is or what the capture #3-of even means. I'd greatly appreciate any help!
Solution
Change the interface to the following:
public List<Interface> getInterfacesOfTypes(List<? extends Class<? extends InternalRadio>> types)
To be quite honest, I cannot really explain why. Broadening the range of allowed generic collections (by adding '? extends') just makes it easier for the compiler to see this is valid...
Aside
Instead of Arrays.asList(type) I would write Collections.singletonList(type).
Prefixing class members with '_' is uncommon in Java
I think Interface is not a great name as 'interface' is also a Java concept (and it seems Interface is not such an interface :) )
I'd probably use an 'getType()' function on Interface instead of directly referring to its '_type' field - this makes for easier refactoring later.
You can probably accept any Collection rather than requiring a List
If you are sure of you object types:
public List<Interface> getInterfacesOfType(final Class<? extends InternalRadio> type)
{
final List list = Arrays.asList(type);
#SuppressWarnings("unchecked")
final List<Class<? extends Interface>> adapters = list;
return getInterfacesOfTypes(adapters);
}

Using the Generic Class from the Method Result of a class

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.

How do I get the type object of a genericized Enum? eg: EnumSet.noneOf(<huh?>)

I have a generic type that is parameterized on some Enum, declared like this:
public class FlagsField<T extends Enum<T>> {
private EnumSet<T> _flagSet;
public FlagsField() {
_flagSet = EnumSet.<T>noneOf( /* what goes here? */ );
}
...
}
I want to initialize _flagsField in the constructor as above, but can't figure out for the life of me what the right parameter to the noneOf method is. It needs to be of type Class<T>. If this weren't a generic, you'd use MyFooEnumType.class here, but T.class is not valid.
Thanks!
You've run into type erasure. Your constructor is going to need to look like:
public FlagsField(Class<T> enumClass) {
_flagSet = EnumSet.<T>noneOf(enumClass);
}
You could use this trick in your constructor: (see Generic Data Access Objects, section "Preparing DAOs with lookup")
enumClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
But I believe this code only works when the class is sub-classed and an instance of the sub-class executes it.

Categories

Resources