Reputation: 55
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.
Upvotes: 2
Views: 135
Reputation: 12932
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());
Upvotes: 3
Reputation: 393791
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);
Upvotes: 8