繁体   English   中英

Java中泛型的静态多态性

[英]Static polymorphism with generics in Java

我希望创建一些干净的代码,可以递归地挖掘集合并打印它找到的第一个Integer。 来自C ++背景的方法看起来像这样:

class Test {
static void print(Integer i) {
  System.out.println(i);
}

static <T> void print(ArrayList<T> arr) {
  T entry= arr.at(0);
  Test.print (entry);
}

static void Test() {
    ArrayList<ArrayList<Integer>> arrayOfArrayOfInt= create();
    print( arrayOfArrayOfInt );
}
}

不幸的是,这不起作用。

一种替代方法是放弃静态多态并创建函数print(Object o),然后执行一些instanceof检查以分支到正确的行为。 这是由于Java的类型擦除是否可以完成的唯一方法,还是有更优雅的方法?

你是正确的假设你不能像处理c ++模板一样对待java泛型。

因此,如果没有instanceof ,这将不会猜测条目是一个整数(既不在运行时也不在编译时):

static <T> void print(ArrayList<T> arr) {
  T entry= arr.get(0);
  Test.print (entry);
}

所以要“创建一些干净的代码,可以递归地挖掘集合并打印它找到的第一个整数” (或者更确切地说是索引0处的第一个非列表元素):

static void print(Object obj) {
    System.out.println(obj);
}

static <T> void print(List<T> arr) {
    T entry = arr.get(0);
    if (entry instanceof List)
        print((List<?>) entry);
    else 
        print(entry);
}

static void test() {
    List<List<Integer>> arrayOfArrayOfInt = create();
    print(arrayOfArrayOfInt);
}

不是您想要的优雅解决方案,而是泛型系统所需的可行解决方案。

下面的方法将递归挖成一个Object ,并返回一个Optional含有第一Integer它找到,或Optional.empty()如果不能找到一个。

static Optional<Integer> firstInt(Object o) {
    if (o instanceof Integer)
        return Optional.of((Integer) o);
    if (o instanceof int[]) {
        int[] array = (int[]) o;
        return array.length > 0 ? Optional.of(array[0]) : Optional.empty();
    }
    if (o instanceof Object[])
        return firstInt(Arrays.asList((Object[]) o));
    if (o instanceof Iterable) {
        for (Object o2 : (Iterable<?>) o) {
            Optional<Integer> result = firstInt(o2);
            if (result.isPresent())
                return result;
        }
    }
    return Optional.empty();
}

可以使用多态做到这一点,但是你必须创建一个像这样的Searchable接口

interface Searchable {
    Optional<Integer> search();
}

然后为您希望能够搜索的所有具体类型创建包装类。 例如:

public final class SearchableList<T extends Searchable> extends AbstractList<T> implements Searchable {

    private final List<T> list;

    SearchableList(List<T> list) {
        this.list = list;
    }

    // rest omitted
}

然而,这将是一个令人费解的混乱,我会避免它,而不是更喜欢检查instanceof

简答:是的,放弃静态多态并使用instanceof 或者只是写一个本地循环来完成你的工作。

Java的泛型比C ++的模板弱得多。 特别是,编译器不会为您生成新的特化。 您可以用它们做的就是对已经通过常规方法定义的类和方法进行更严格的静态类型检查(例如,您手动编写它们或使用工具生成它们)。 如您所料,类型擦除是相关的。

使用Java泛型,此代码:

static <T> void print(ArrayList<T> arr) {
  T entry= arr.at(0);
  Test.print (entry);
}

由于类型擦除,总是有效地转入此:

static Object void print(ArrayList<Object> arr) {
  Object entry= arr.at(0);
  Test.print (entry);
}

它不会像C ++模板那样产生单独的特化。

您的特定代码无法正常工作,因为它缺少一些特定的方法,您必须手动提供这些方法。 但我想我能看到你想要去的地方。 (我假设您希望最终得到的代码可以在更广泛的类型上工作,而不仅仅是List<List<Integer>> 。)并且您不会使用Java泛型获得满意的解决方案。 (你可以使它工作,但你必须手工编写大量的代码,哪些模板对你没用。而且会有一些grody edge例。)切换到动态类型检查和instanceof ; 这是Java方法来做这些事情。

首先,获取元素的函数是“get”而不是“at”。 但是,你的主要问题是你试图调用带有整数的函数“print”,但是传递一个类型为“T”的值。

如果要调用print并将其传递给未知类型的值,则该函数必须采用Object类型的参数或采用泛型参数,例如

public <T> void print(T item)

要么

public void print (Object item)

Java只会在编译时将函数调用与最具体的重载进行匹配,如果您在运行时尝试查找正确的重载,则必须使用instanceof。 或者,您可以构建一个从Class对象指向Consumer对象的HashMap。

HashMap<Class<?>, Consumer<?>> prints = new HashMap<>();
void setup() {
    prints.put(ArrayList.class, (list) -> {
        for (Object o : list) {
            print(o);
        }
    }

    prints.put(Integer.class, (integer) -> System.out.println(integer));
}

void print(Object o) {
    Consumer<?> func = prints.get(o.getClass());
    func.accept(o);
}

如果要保持此代码的方式,则必须为对象添加另一种打印方法。

public static void print(Object o){}

但无论如何,你的例子有更好的方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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