0

Just start to learn and know about java reflection, would appreciate any help on this problem.

I'm trying to write a method and it looks like this:

    private <T> void myMethod (List<T> testSub) {
        testSub.forEach(s -> assertEquals(s.getSource(), "TEST"));
    }

But it shows an error because java don't know if testSub's class has getSource() this method and it want me to cast s.getSource() to a known class.

What I want is to somehow let java know that the the itmes in testSub's class has this getSource() method and it could be invoked and safe to call.

Thanks in advance!

----------------------------Update---------------------- Choose to use this way:

    private <T> void myMethod (List<T> testSub, Class<T> clazz) {

        testSub.forEach(s -> assertEquals(clazz.getMethod("getSource").invok(s), "TEST"));
    }

Has to Catch exceptions in for it but works.

2
  • There's no use of reflection in your example code. Do you mean generics? Commented Jan 9, 2018 at 18:29
  • "Has to Catch exceptions in for it but works": as this looks like a test case, I hope you re-throw the exceptions or fail() in case of an exception. Commented Jan 11, 2018 at 9:42

2 Answers 2

1

Add a bound to your type variable definition:

private <T extends HasSource> void myMethod (List<T> testSub) {

where HasSource defines getSource() method:

public interface HasSource {
    String getSource()
}

The compiler will know that T is actually a subclass of HasSource and hence that it has getSource() method.

Of course, to leverage this solution, you would need to make all the classes you want to use as elements of testSub implement that HasSource interface.

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

5 Comments

That's not what I want, the only two class will be passed to myMethod is already defined and both of them has getSource() method.
@DawnZHANG, Roman has presented an example, not an exact solution. You must choose a bound that is an actual supertype of the classes of all the objects in your List.
@DawnZHANG you need to somehow tell the compiler that T presents not just any class but some class that has getSource() method. To make it work for both your classes, you need to either make them extend a common base class (or implement an interface) or make one of them extend another, and then use the parent class defining getSource() as type bound.
There's no way I can do something like : testSub.get(0).getClass() to get the class of whatever passed in?
Sure you can, but then you will have to match it with your known classes and cast the element to one of them (the one that matches). Generics were introduced, partially, to avoid such casting. Another option is to use reflection which is pretty ugly and unsafe.
0

There are two ways that you can call the getSource() method.

One is the normal method call expression like object.getSource(). That's only possible if the compiler knows that object is of a class that has a getSource() method.

In your case, the compiler only knows s to be of the generic type T, which is unbounded, so can be any class, even Object, and the compiler can't confirm that there is a getSource() method.

If you don't have a common parent class or interface for your list elements having the getSource() method, you could go for instanceof / cast combinations. That's ugly but might work for you if you really know the possible element classes.

for (T s : testSub) {
    if (s instanceof Class1) {
        assertEquals(((Class1) s).getSource(), "TEST"));
    } else if (s instanceof Class2) {
        assertEquals(((Class2) s).getSource(), "TEST"));
    } else {
        throw new IllegalArgumentException();
    }
}

The other way is using reflection. You could locate the Method m = s.getClass.getMethod("getSource") and, if not null, do m.invoke(s). But that's even uglier than the instanceof / cast solution, and surely a lot slower.

2 Comments

I'm using Method m = s.getClass.getMethod("getSource"), but why is that slower?
@DawnZHANG It's slower because in every iteration it has to search the class plus its superclasses for a method with a given name and invoke that method dynamically. In addition, you have two completely distinct getSource() methods, making it impossible for the HotSpot engine to do any optimizations like inlining.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.