简体   繁体   中英

Use Class parameter for generic type (e.g ArrayList)

I am trying to create a generic method to handle different types of ArrayLists in Java. The types are quite different, but all contain one identical parameter, which I want to evaluate in this method.

But I cannot create proper ArrayList<type> with given class information.

What am I doing wrong?

private String test(ArrayList<?> list, Class<?> type) {
    for(type i : list){  // for (type i : (ArrayList<type>) list){
     // do something
    }
    return "xxx"
}

private void init() {
    ArrayList<Type_1> a1 = new ArrayList<>();
    ArrayList<Type_2> a2 = new ArrayList<>();

    String s1 = test(a1, Type_1.class);
    String s2 = test(a2, Type_2.class);
}

Update Found a solution

private String test(ArrayList<?> list) {
    for (Object i : list){
        try {
            Method m = i.getClass().getMethod("getName", null);
            System.out.println(m.invoke(i));
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
            //Handle Exception
        }
    }
 }

You need to declare an explicit type parameter:

  private <T> String test(ArrayList<T> list, Class<T> type) {
      for (T i : list) {
         // do something with 'i'
      }
      return "xxx"
  }

If you don't need Class<T> argument, you can leave it out; eg

  private <T> String test(ArrayList<T> list) {
      for (T i : list) {
         // do something
      }
      return "xxx"
  }

You would normally only need to pass a Class<T> type object if you intended to create instances of that class ... or an array of that class.


The commented out version of your code would not compile because it is mixing compile-time and runtime types. In

for (type i : (ArrayList<type>) list) {

the compiler would need to know what type was represented by type at compile time. But it is a runtime variable. But that is invalid even before that because the Java syntax requires the identifier for a type at that point ... not the identifier for a variable.

I created a mixed answer relating to both solutions:

Implementing Interfaces

The first is done with a simple interface MyType that defines the contract method(s) for the classes Type_1 and Type_2 . This is the clean and proper Java way. This way the compiler will already tell you if you can do certain operations or not. This also shows beginners problems with their concept of implementing ideas.

The downside is that all classes have to implement that interface (which may be defined anywhere along their inheritance hierarchy).

But Java is all about the advantage of type safety and compiler warnings. So this is clearly the preferred way.

Using Reflection

Using Reflection for this task is possible, yes. But not necessarily a good idea. With reflection, there are multiple problems:

  • You will get runtime errors or have to handle those exceptions
  • The project will start, but if your design, your concept, is flawed, this will be a lot harder to pinpoint
  • lots of libraries cannot handle reflections well (Aspect Oriented Programming, Application Containers, special Compilers like the GraalVM NativeImage, etc etc)
  • Using reflection will be slower and consume more memory

So if it's possible and easy to stay away from Reflection, you should steer clear. Especially if this has such a simple proper solution to it.

(I also cleaned up your code with the reflection, there were some minor inconsistencies in there that wouldn't let it compile)

Code:

package stackoverflow;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class SimpleInterfacing {
    interface MyType {
        String getName();
    }
    static class Type_1 implements MyType {
        @Override public String getName() {
            return "This is type 1";
        }
    }
    static class Type_2 implements MyType {
        @Override public String getName() {
            return "This is type 2";
        }
    }

    private String test(final ArrayList<? extends MyType> list) {
        String returnValue = null;
        for (final MyType t : list) {
            // do something
            System.out.println("Got name: " + t.getName());
            returnValue = t.getName();
        }
        return returnValue; // returns last value, null is lists are empty
    }

    private void init() {
        final ArrayList<Type_1> a1 = new ArrayList<>();
        a1.add(new Type_1());

        final ArrayList<Type_2> a2 = new ArrayList<>();
        a2.add(new Type_2());

        {
            final String s1 = test(a1);
            System.out.println("s1 is " + s1);
        }
        {
            final String s2 = test(a2);
            System.out.println("s2 is " + s2);
        }
        {
            test_reflection(a1);
            test_reflection(a2);
        }
    }

    public static void main(final String[] args) {
        new SimpleInterfacing().init();
    }

    private String test_reflection(final ArrayList<?> list) {
        for (final Object i : list) {
            try {
                final Method m = i.getClass().getMethod("getName");
                System.out.println("Invoked: " + m.invoke(i));
            } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
                //Handle Exception
                ex.printStackTrace();
            }
        }
        return null;
    }



}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM