简体   繁体   English

Java Generics:转换列表<typea>列出<typeb></typeb></typea>

[英]Java Generics: convert List<TypeA> to List<TypeB>

this is a basic question, i guess.这是一个基本问题,我猜。 However, coming from C++ this is not as easy as i expected.然而,来自 C++ 这并不像我预期的那么容易。 Please consider the following code:请考虑以下代码:

import java.util.ArrayList;
import java.util.List;

final class TestClass {

    public class Foo {
        int myInt;
        float myFloat;
    }

    public class Bar {
        int myInt;
        float myFloat;
    }

    private static Bar toBar(Foo in) {
        Bar out = new Bar();
        out.myInt = in.myInt;
        out.myFloat = in.myFloat;
        return out;
    }

    public static <InT, OutT> List<OutT> toBar(List<InT> in) {
        List<OutT> out = new ArrayList<>(in.size());
        for (InT inElement: in) {
            out.add(toBar(inElement));
        }
        return out;
    }
}

This gives me an error: " error: no suitable method found for toBar(InT)".这给了我一个错误:“错误:找不到适合 toBar(InT) 的方法”。

I have a lot of collections, that need to be converted from List<TypeA1> to List<TypeB1> , List<TypeA2> to List<TypeB2> .我有很多 collections,需要从List<TypeA1>转换为List<TypeB1>List<TypeA2>转换为List<TypeB2> To reduce code, i wanted to extract the list iteration in a Java Generic, while doing the element conversion in a concrete implementation.为了减少代码,我想在 Java Generic 中提取列表迭代,同时在具体实现中进行元素转换。 Any ideas, how to do this?任何想法,如何做到这一点? Unfortunately, third-party libs are not allowed.不幸的是,第三方库是不允许的。

Thanks a lot.非常感谢。

Your static method doesn't pass compilation because the toBar method you are calling expects a Foo argument, not a generic type parameter that can be anything.您的static方法没有通过编译,因为您调用的toBar方法需要一个Foo参数,而不是可以是任何东西的泛型类型参数。

You don't need a method to do the conversion.您不需要方法来进行转换。 You can achieve the same with a relatively short Stream pipeline:您可以使用相对较短的Stream管道实现相同的目的:

List<Foo> foos = ...
List<Bar> bars = foos.stream().map(TestClass::toBar).collect(Collectors.toList());

Of course, if you must, you can wrap this in a method, but you'll have to pass a Function that tells the method how to convert an element of the first List to an element of the second List :当然,如果必须,您可以将其包装在一个方法中,但您必须传递一个Function告诉该方法如何将第一个List的元素转换为第二个List的元素:

public static <InT, OutT> List<OutT> convert(List<InT> in, Function<InT,OutT> converter) {
    return in.stream().map(converter).collect(Collectors.toList());
}

And you call it with:你用它来称呼它:

List<Foo> foos = ...
List<Bar> bars = convert(foos,TestClass::toBar);

Despite the syntactical similarity, C++ templates and Java generics are very different.尽管语法相似,但 C++ 模板和 Java generics 非常不同。

In C++, the templates are instantiated at compile time when the templates are used.在 C++ 中,模板在使用模板时在编译时实例化。 So if I write toBar(listOfFoos) , the compiler starts compiling the toBar(List<Foo>) method.所以如果我写toBar(listOfFoos) ,编译器就会开始编译toBar(List<Foo>)方法。 At this point it knows that it needs to call toBar(Foo) .此时它知道它需要调用toBar(Foo) If I write toBar(listOfBazs) a few lines later, it compiles an additional toBar(List<Baz>) method, in which an entirely different method can be called.如果我在几行之后编写toBar(listOfBazs) ,它会编译一个附加的toBar(List<Baz>)方法,其中可以调用一个完全不同的方法。

In Java, on the other hand, the method is compiled only once.另一方面,在 Java 中,该方法只编译一次。 At compile time it must be clear which method is to be called.在编译时,必须明确要调用哪个方法。 And in your case, it cannot find a matching method.在您的情况下,它找不到匹配的方法。

One option would be to pass the method as a parameter:一种选择是将方法作为参数传递:

 public static <InT, OutT> List<OutT> toBar(List<InT> in, Function<InT, OutT> fn) {
    List<OutT> out = new ArrayList<>(in.size());
    for (InT inElement: in) {
        out.add(fn.apply(inElement));
    }
    return out;
}

and call it with并调用它

result = toBar(listOfFoos, TestClass::toBar);

Or you use a stream:或者您使用 stream:

result = listOfFoos.stream()
                   .map(TestClass::toBar)
                   .collect(Collectors.toList());

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

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