I am writing a method with a generic List<T> as an argument. I want to limit T to Integer, Float and Double with this:
private Method(List<T> list) {
this.list = list;
}
public static <T extends Integer> Method<T> create(List<T> list) {
return new Method<>(list);
}
public static <T extends Float> Method<T> create(List<T> list) {
return new Method<>(list);
}
public static <T extends Double> Method<T> create(List<T> list) {
return new Method<>(list);
}
But I get this error:
error: name clash: <T#1>create(List<T#1>) and <T#2>create(List<T#2>) have the same erasure
public static <T extends Float> Method<T> create(List<T> list) {
^
where T#1,T#2 are type-variables:
T#1 extends Float declared in method <T#1>create(List<T#1>)
T#2 extends Integer declared in method <T#2>create(List<T#2>)
I get the same error for T#1 extends Double as well.
The code is based on this answer, which works well. So I think the problem is related to the fact that I used a list of generics as an input instead of a single generic.
How can I fix this? Is there some way to give Java the ability to discern between the different instances?
You could use the Number superclass of Integer, Float, and Double as your bound.
public class Method<T extends Number> {
private final List<T> list;
public Method(List<T> list) {
this.list = list;
}
public static void main(String[] args) {
var mFloats = new Method(Arrays.asList(1.0f, 2.0f, 3.0f));
var mDoubles = new Method(Arrays.asList(1.0,2.0,3.0));
var mInts = new Method(Arrays.asList(1,2,3));
}
}
Type Erasure affects Generic Collections like List, so each generic method is type erased into having a parameter signature with parameter type Obejct. See Oracle Docs To avoid this you can use arrays instead.
import java.util.Arrays;
import java.util.List;
public class Method<T> {
private final List<T> list;
private Method(List<T> list) {
this.list = list;
}
public static <T extends Integer> Method<T> create(T[] arr) {
return new Method<>(Arrays.asList(arr));
}
public static <T extends Float> Method<T> create(T[] arr) {
return new Method<>(Arrays.asList(arr));
}
public static <T extends Double> Method<T> create(T[] arr) {
return new Method<>(Arrays.asList(arr));
}
public static void main(String[] args) {
var floatMethod = Method.create(new Float[] {1.0f, 2.0f});
var doubleMethod = Method.create(new Double[] {1.0, 2.0});
var intMethod = Method.create(new Integer[] {1, 2});
}
}
Related
public class Foo<T> {
public static <T> Foo<T> newFoo() {
return new Foo<>();
}
public Bar<T, T> toBar() {
return new Bar<>(this, new ArrayList<T>());
}
}
public class Bar<S, T> {
public Bar(Foo<T> Foo, List<S> list) {
}
public static void main(String[] args) {
Foo<Integer> newFoo = Foo.newFoo();
Bar<Integer, Integer> s = newFoo.toBar();
Bar<Integer, Integer> s2 = Foo.newFoo().toBar();
}
}
The first two lines of the main method work fine. The last line (Foo.newFoo().toBar()) gives me an error: Type mismatch: cannot convert from Bar<Object,Object> to Bar<Integer,Integer>. Is there a way to this in one line without getting an error? Casting to Bar<Integer, Integer> doesn't work.
More out of curiosity than necessity...
This works:
Bar<Integer, Integer> s2 = Foo.<Integer>newFoo().toBar();
In a test class using AssertJ, I have code similar to the following:
public void someTest() {
assertThat(getNames()).has(sameNamesAs(getExpectedNames()));
assertThat(getNames()).doesNotHave(sameNamesAs(getOtherNames()));
}
private List<String> getNames() {
return null;
}
private List<String> getExpectedNames() {
return null;
}
private List<String> getOtherNames() {
return null;
}
private Condition<List<String>> sameNamesAs(List<String> rhs) {
return new Condition<List<String>>("same names as " + rhs) {
#Override
public boolean matches(final List<String> lhs) {
return lhs.containsAll(rhs) && rhs.containsAll(lhs);
}
};
}
I get a compilation error on the calls to has and doesNotHave:
has/doesNotHave
(org.assertj.core.api.Condition<? super java.util.List<? extends java.lang.String>>)
in AbstractListAssert cannot be applied
to
(org.assertj.core.api.Condition<java.util.List<java.lang.String>>).
I'm new to Java and I don't understand the problem: java.util.List is a super-type of java.util.List and java.lang.String extends java.lang.String, don't they?
In your case, the has and doesNotHave methods take a Condition<? super List<? extends T> condition, not a Condition<? super List<T>> as you are returning from your Condition<List<T>> sameNamesAs method.
You need an instance of the Condition<List<? extends String>> type (it's a subclass of the original type Condition<? super List<? extends String>>):
private Condition<List<? extends String>> sameNamesAs(List<String> rhs) {
return new Condition<List<? extends String>>("same names as " + rhs) { ... };
}
I tried to illustrate this with the following snippet:
List<String> list = getNames();
// ELEMENT = String, ACTUAL = List<? extends ELEMENT>
ListAssert<String> assertThat = assertThat(list);
// by the signature, we have to pass Condition<? super ELEMENT> or Condition<? super ACTUAL>
// Condition<? super ACTUAL> = Condition<? super List<? extends String>>
Condition<List<? extends String>> condition = sameNamesAs(list);
// Condition<List<? extends String>> extends Condition<? super List<? extends String>>
assertThat.has(condition);
When I try to set a parameter as extending number using wildcards as shown in the code below -
import java.util.*;
class Try {
public static void main(String [] args) {
List <Integer>a = new ArrayList<>();
a.add(1);
a.add(2);
a.add(3);
System.out.println(foo(a));
}
public static double foo(List<? extends Number> list) {
double x = 0.0;
for (Object e : list)
x += (Integer) e;
return x;
}
}
It compiles fine.
However, If i do the same using a generic type, as shown here -
import java.util.*;
class Try {
public static void main(String [] args) {
List <Integer>a = new ArrayList<>();
a.add(1);
a.add(2);
a.add(3);
System.out.println(foo(a));
}
public static <T extends Number> double foo(List<T extends Number> list) {
double x = 0.0;
for (Object e : list)
x += (Integer) e;
return x;
}
}
I get the following syntax error -
Try.java:12: error: > expected
public static <T extends Number> double foo(List<T extends Number> list) {
^
Try.java:12: error: ')' expected
public static <T extends Number> double foo(List<T extends Number> list) {
^
Try.java:12: error: ';' expected
public static <T extends Number> double foo(List<T extends Number> list) {
^
Try.java:12: error: <identifier> expected
public static <T extends Number> double foo(List<T extends Number> list) {
^
4 errors
Why does the second case give me an error? What is the difference between generics and wildcards in this case?
What you wrote is invalid syntax. This is what you intended:
public static <T extends Number> double foo(List<T> list) {
That is, the extends belongs to the type token definition, not the type declaration in the method parameter list.
Assuming that in your second piece of code you meant this:
public static <T extends Number> double foo(List<T> list)
There is no difference between the two declarations. Any type that can be passed to one function signature can be passed to the other, and vice versa. Every possible type List<T> where T is a non-strict subtype of Number, is a subtype of List<? extends Number>. And List<? extends Number> can be captured into List<T> with T extends Number. So the two are completely equivalent.
I'm having troubles trying to find a solution, if any, to this:
public class Generics {
Map<Class<? extends SomeObject1>, SomeObject2>> map;
map = new HashMap<Class<? extends SomeObject1>, SomeObject2>>();
public static <E extends SomeObject1> SomeObject2 get(Class<E> c) {
if (map.containsKey(c))
return map.get(c);
else {
SomeObject2 o = new SomeObject2();
map.put(c, o);
return o;
}
}
}
...
//somewhere
public <T extends SomeObject1> void aMethod(AnInterestedClass<T> list) {
// How to get the value from the map
// knowing that the key is of type T?
Generics.get();
}
Ideas?
Because of type erasure, you can only do this by passing a Class object to aMethod. See this related thread.
I'm trying to make this code as generic as possible, but im stuck right on the last part. This is where my code is called:
List<Integer> NewList = map(OriginalList, new IFunction<Integer>(){
public <T extends Number> int execute(T anInt){
return anInt.intValue() + 1;
}
});
then I have the method map:
public static <T> List<Integer> map(List<T> c, IFunction<T> f) {
List<Integer> TempList = new ArrayList<Integer>();
for (T o : c){
TempList.add(f.execute(o));
}
return TempList;
}
and the interface IFunction:
public interface IFunction<T> {
public <T extends Number> int execute(T o);
}
my error is in Map() where it says TempList.add(f.execute(o)); i am trying to declare the TempList to be of type T and the execute method to return an incremented number in Type T.
Every time i fix one part of the code i seem to have ruined another part. Ideally all parameters would be generic and there would be no 'Integer' anywhere except where i call my code
You need to constrain your parameter in the map() method:
public static <T extends Number> List<Integer> map(List<T> c, IFunction<T> f) {
...
Otherwise f.execute() will complain that the type of the argument can be anything, and it expects a Number.
Try this:
IFunction.java
public interface IFunction <T extends Number> {
T execute(T obj);
}
Main.java
public class Main {
public static void main(String[] args) {
List<Integer> originalList = new ArrayList<Integer>();
List<Integer> newList = map(originalList, new IFunction<Integer>(){
public Integer execute(Integer anInt){
return anInt.intValue() + 1;
}
});
}
public static <T extends Number> List<T> map(List<T> c, IFunction<T> f) {
List<T> tempList = new ArrayList<T>();
for (T o : c){
tempList.add(f.execute(o));
}
return tempList;
}
}
You should try a different Generic Setup:
public interface IFunction<T extends Number> {
public int execute(T o);
}
List<Integer> NewList = map(OriginalList, new IFunction<Integer>(){
public int execute(Integer anInt){
return anInt.intValue() + 1;
}
});
public static <T extends Number> List<Integer> map(List<? extends T> c, IFunction<T> f) {
List<Integer> tempList = new ArrayList<Integer>();
for (T o : c){
tempList.add(f.execute(o));
}
return tempList;
}
This is as close as I could get to removing Integer (changing variable names to start lower case):
public class Main
{
public static void main(String[] args)
{
List<Integer> originalList = new ArrayList<Integer>();
originalList.add(1);
originalList.add(2);
originalList.add(3);
originalList.add(4);
List<Integer> newList = map(originalList, new IFunction<Integer>()
{
public <T extends Number> T execute(T aNumber)
{
Integer result = aNumber.intValue() + 1;
return (T) result;
}
});
System.out.println(newList);
}
public static <T extends Number> List<T> map(List<T> c, IFunction<T> f)
{
List<T> tempList = new ArrayList<T>();
for (T number : c)
{
tempList.add(f.execute(number));
}
return tempList;
}
}
and
public interface IFunction<T> {
public <T extends Number> T execute(T o);
}
Still got one inside the implementation of execute().