简体   繁体   中英

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. 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)".

I have a lot of collections, that need to be converted from List<TypeA1> to List<TypeB1> , List<TypeA2> to 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. 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.

You don't need a method to do the conversion. You can achieve the same with a relatively short Stream pipeline:

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 :

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.

In C++, the templates are instantiated at compile time when the templates are used. So if I write toBar(listOfFoos) , the compiler starts compiling the toBar(List<Foo>) method. At this point it knows that it needs to call 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.

In Java, on the other hand, the method is compiled only once. 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:

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

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