【问题标题】:Pass generic type class method as parameter将泛型类型类方法作为参数传递
【发布时间】:2017-03-24 09:52:32
【问题描述】:

有没有办法将泛型类型的方法作为参数传递? 我试图实现的是这样的:

ListUtils.mapObjectToField( entries, Contact.getZipCode );

我传递了一个List<T> 和一个来自泛型类T 的方法。 到目前为止,我对通用 List 很好,但我无法实现方法调用。

public static <T, U> List<U> mapObjectToField( List<T>elements, /*generic method*/ )
{
    List<U> result = new ArrayList<>();

    for ( T current : elements )
    {
        result.add(current./*generic method*/);
    }
    return result;
}

没有 Java 8,因为我正在为 Android min SDK 14 编码。

【问题讨论】:

  • 如果您将方法的名称作为字符串传递,您可以使用反射来实现,但它不会很漂亮。
  • 当你实现要在别处执行的实际代码时,你为什么需要这个方法呢?
  • 我建议你看看 java.util.function.Function 它几乎可以满足你的需求。

标签: java android list generics reflection


【解决方案1】:

可能最好的方法是像 Java 8 中的 Stream.map 那样实现它并提供您自己的接口,可能称为 Mapper,但要在调用站点提供实现,您必须使用匿名内部班级。 Lambda 更好。

它可能看起来像这样:

public interface Mapper<T, U> {
    U map(T t);
}

ListUtils 会这样:

public class ListUtils {
    public static List<U> mapObjectToField(List<T> elements, Mapper<T, U> mapper) {
        List<U> result = new ArrayList<>();
        for (T current : elements) {
            result.add(mapper.map(current));
        }
        return result;
    }
}

你可以这样称呼它:

List<String> zipCodes = ListUtils.mapObjectToField(contacts,
    new Mapper<Contact, String>() {
        public String map(Contact contact) {
            return contact.getZipCode();
        }
    });

您可以在其他地方定义映射器,这会稍微清理外观,但不如使用 lambdas 好。

【讨论】:

  • 虽然它并没有真正回答传递方法的问题,但这正是我想要实现的,非常感谢!
【解决方案2】:

如果你想使用反射就这样做(但不是推荐的解决方案),

public static <T, U> List<U> mapObjectToField( List<T>elements, String methodName )
    {
        List<U> result = new ArrayList<>();
        Method method = null;

        for ( T current : elements )
        {
            if(method == null){
                try {
                    method = current.getClass().getDeclaredMethod(methodName);
                } catch (Exception e) {
                    e.printStackTrace();
                } 
            }

            U object = (U) invokeMethod(current, method);
            result.add(object);
        }
        return result;
    }


public static Object invokeMethod(Object obj , Method method)
    {
        if (method != null) {
            try {
                Object value = method.invoke(obj);
                return value;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

【讨论】:

    【解决方案3】:

    您也可以在 android SDK 14 中使用 Retrolambda 来获得 lambda 和方法引用。

    https://github.com/evant/gradle-retrolambda

    https://stackoverflow.com/a/42996933/802034 中建议的代码将变为:

      public void main(){
        List<Integer> lengths = mapObjectToField(nCopies(5, "hello"), String::length);
      }
    
      public static <T, U> List<U> mapObjectToField(List<T> elements, Mapper<T, U> mapper) {
        List<U> result = new ArrayList<>();
        for (T current : elements) {
          result.add(mapper.map(current));
        }
        return result;
      }
    
      public interface Mapper<T, U> {
        U map(T t);
      }
    
    
    }
    

    如果你想更进一步并使用 Streams,你可以在 android 上使用这个库:https://github.com/aNNiMON/Lightweight-Stream-API

     List<Integer> lengths =
            Stream.of(nCopies(5, "hello"))
                  .map(String::length)
                  .collect(Collectors.toList());
    

    【讨论】:

      猜你喜欢
      • 2019-02-27
      • 1970-01-01
      • 1970-01-01
      • 2019-03-05
      • 1970-01-01
      • 1970-01-01
      • 2010-11-15
      • 2013-05-05
      • 1970-01-01
      相关资源
      最近更新 更多