0

I'm trying to create a function that takes in an instance of a parameterized class with one type parameter T, and then uses reflection to look at fields of its enveloping class, checks if those fields are of type T, and then casts and does something with them if they are. Here's what I have so far:

 private <T> void someFunction(SomeClass<T> instance) throws IllegalAccessException {
            for(Field field : this.getClass().getDeclaredFields()){
                Object obj = field.get(this);
                // TODO: get parameter T from instance and store it in a variable named 'myClass'
                if(myClass.isInstance(obj)){
                    doSomething(instance, myClass.cast(obj))
                }
            }
        }

From what I understand, in order to do this at runtime you need to declare a variable of type Class and store the class of T in there so you can then use the isInstance() and cast() functions, but I am unsure how to properly do this.

4
  • to do this at runtime you need to declare a variable of type Class and store the class of T in there so you can then use the isInstance() and cast() functions You'll need to clarify, because this is too vague. Do you have a source for that statement? Commented Feb 15, 2021 at 6:08
  • 2
    T is a type parameter. There is no “class of T”. The actual argument for T can be something like ? extends Comparable&CharSequence. No Class object can represent this. Commented Feb 15, 2021 at 14:25
  • @Holger Oh ok that makes sense, but looking at another post, in the answers I found this code snippet: ParameterizedType pt = (ParameterizedType)this.getClass().getGenericSuperclass(); clazz = (Class<?>)pt.getActualTypeArguments()[0]; Though when I tried to do that, I got the error java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class. Is it possible to do something similar to get a single class from the type parameter? Commented Feb 15, 2021 at 17:49
  • 2
    That Type may be a Class in some scenarios, but it isn’t in others. When you have a class of the form class Sub<T> extends Super<T>, then getGenericSuperclass().getActualTypeArguments()[0] can’t be a Class, as the type argument for Super is the T type variable declared by Sub, not a concrete class. Since someFunction is generic, the caller of that function will decide which type argument to use. Nothing in SomeClass can tell you what the immediate caller of someFunction did. Commented Feb 16, 2021 at 7:22

1 Answer 1

1

Short Story -- Missing Piece

private <T extends SomeClass> void someFunction(SomeClass<T> instance) throws IllegalAccessException {
    for (Field field : this.getClass().getDeclaredFields()) {
        Object obj = field.get(this);

        Class<?> objClass = obj.getClass();
        if (objClass.isInstance(instance))
            doSomething(instance, (T) obj);
    }
  • Where we get and store the Class from the Object, then check that against the instance we want to match with.
  • We use the wildcard ? to signify an unknown Type, which saves us from having to figure out, or specify types.
  • Then we assure the doSomething Method accepts the same generic type argument to allow for similar work
    • Therefore we only need to cast to T

As a Whole, With A Working Example of Animals

(Yes this is a reduction :))

Animal Class as a Base

public abstract class Animal {

    protected Object color;

    protected Animal(Object color) {
        this.color = color;
    }

    protected Animal() {}

}

Various Colors of Animal (To Simplify)

No Color Animal (A Ghost?!)
public class NoColorAnimal extends Animal {

    // A null color
    public NoColorAnimal() {}

}
A Single Color Animal
public class SingleColorAnimal extends Animal {

    // A single String of color
    public SingleColorAnimal(String color) {
        super(color);
    }

}
A Multi-colored Animal
public class MultiColorAnimal extends Animal {

    // An Array of colors
    public MultiColorAnimal(String... colors) {
        super(colors);
    }

}

A Zoo With Viewable Animals as Your Enclosing Class

public class ZooAnimals {

    public MultiColorAnimal tiger = new MultiColorAnimal("black", "orange");
    public SingleColorAnimal dolphin = new SingleColorAnimal("grey");


    public <T extends Animal> void viewAnimalIfPresent(Animal<T> animal) throws IllegalAccessException {
        for (Field field : this.getClass().getDeclaredFields()) {
            Object obj = field.get(this);

            Class<?> objClass = obj.getClass();
            if (objClass.isInstance(animal))
                view(animal, (T) obj, field.getName());
        }
    }

    private <T extends Animal> void view(Animal<T> animal, T obj, String name) {
        // TODO - Do something useful with these, but simply log them for now :)
        System.out.printf("Viewed a %s; the %s%n", obj.getClass().getSimpleName(), name);
    }

}

A Simple Test

ZooAnimals zoo = new ZooAnimals();

    @Test
    public void test() throws IllegalAccessException {
        zoo.viewAnimalIfPresent(zoo.tiger);
        zoo.viewAnimalIfPresent(zoo.dolphin);
        zoo.viewAnimalIfPresent(new NoColorAnimal());
    }

Result of Test

The Console Ouput for Test Showing Proper Type Shifting
Viewed a MultiColorAnimal; the tiger
Viewed a SingleColorAnimal; the dolphin

Where no field matched a colorless Animal, and therefore nothing was printed.

Sign up to request clarification or add additional context in comments.

2 Comments

The type parameter of your Animal class has no relevance to the code you’ve posted. You never check that an object matches the actual type of T, it’s just a coincidence that the classes always declare the actual type argument to be matching. I could easily add another class that does not match its type argument for T, and the test would not detect that they don’t match, but it has no consequence anyway.
Fair point. I thought to add it as demonstration of how one can directly utilize sub-classes from super-classes, but only did it to this stage so as not to make it overly complex, then left the comment. As such, you're right though that it adds nothing if it shows/affects nothing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.