Need some slight help on this generic Java - java

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

Related

Java generic List name clash, has same erasure

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});
}
}

java PECS List<? super T> dest and List<T> dest difference?

public static <T> void copy1(List<? extends T> src, List<? super T> dest) {
for (int i = 0; i < src.size(); i++) {
dest.add(src.get(i));
}
}
public static <T> void copy2(List<? extends T> src, List<T> dest) {
for (int i = 0; i < src.size(); i++) {
dest.add(src.get(i));
}
}
top 2 method both can implements PECS Effect,but what difference ??
thank you !!
PECS -> Producer Extends Consumer Super
? extends T -> from the list perspective, it acts as a producer to others. You can get items from it (list produces), but you can’t insert into it. In Java world, it is called covariance.
? super T-->This is called consumer behavior, because, from the list perspective, it allows to add items to it (list consumes), but not useful in type-safety reading (producing). It's contravariance.
T -->In here, we can insert any subtypes of T class into the list. And when we reading, it will return a type of T instance. This is called Invariance.
class A{
public int a;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
}
class B extends A
{
public int b;
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
}
public class GenericsDemo {
public static void main(String[] args) {
List<A> invariant = new ArrayList<>();
List<? extends A> covariant = new ArrayList<>();
List<? super A> contravariant = new ArrayList<>();
invariant.add(new B());
invariant.get(0).getA();
//Error as it expect the instance of A while reading
// invariant.get(0).getB();
//Error it will be used to read the values, we can't insert any values in it.
// We will use this construct places like defining params to a function
//covariant.add(new B());
//covariant.add(new A());
contravariant.add(new A());
contravariant.add(new B());
//Error, it will be used to write only
//While reading, it will give you object type
//contravariant.get(0).getA();
//We can type-cast while reading
A a = (A) contravariant.get(0);
a.getA();
copy1(invariant,invariant);
copy2(invariant,invariant);
//Error due to the dest param requires super of T
// copy1(covariant,covariant);
//Error due to the dest param requires of T
//copy2(covariant,covariant);
copy1(covariant,contravariant);
copy2(covariant,contravariant);
}
public static <T> void copy1(List<? extends T> src, List<? super T> dest) {
for (int i = 0; i < src.size(); i++) {
dest.add(src.get(i));
}
}
public static <T> void copy2(List<? extends T> src, List<T> dest) {
for (int i = 0; i < src.size(); i++) {
dest.add(src.get(i));
}
}
}
Method 1 -- > public static void copy1(List src, List dest)
dest list will consume anything with type T and it's sub class. But, when we read it, it will return an instance with Object type which we need to be type caste.
Methpd 2---> public static void copy2(List src, List dest)
dest list will consume anything with type T and it's sub class. But, when we read it, it will return the instance of T
Reference --> https://medium.com/#isuru89/java-producer-extends-consumer-super-9fbb0e7dd268
PS:- Sorry for long answer.
In this specific example both methods are the same, because the type T is unbounded itself. Thus, both declarations just say that the dst parameter lists elements must be a superclass of src parameter lists elements, using ? super T in copy1() does not add something new here.
PECS rule does not mean you must always put extends or super everywhere. Here is the complete example of PECS-compatible declaration from Item 32 of J. Bloch "Effective Java":
public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2);
No need to use a return type as Set<? super E> here.

Generic specification in chained method call

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();

Java abstract class with generic lists

I'm trying to define an abstract class that takes in a List of things and does stuff. Something like:
abstract public class AbstractClass {
private final List<?> list;
public AbstractClass(List<?> list) {
this.list = list;
}
public List<?> getList() { return list; }
abstract void addToList(List<?> list);
}
public class Class1 extends AbstractClass {
public Class1(List<Integer> list) {
super(list);
}
#Override
void addToList(List<Integer> list) {
// do stuff
}
}
public class Class2 extends AbstractClass {
public Class2(List<String> list) {
super(list);
}
#Override
void addToList(List<String> list) {
// do stuff
}
}
List<Integer> a = new List<Integer>();
Class1 c1 = new Class1(a);
List<Integer> b = c1.getList();
c1.addToList(a);
List<String> c = new List<String>();
Class2 c2 = new Class2(c);
List<Integer> d = c2.getList();
c2.addToList(c);
Having addToList with specific types in the subclasses is generating errors, but I don't know if that's a matter of syntax (with my poor knowledge of generics) or what I'm trying to do itself (is it possible to overload an abstract method like that)?
You should make AbstractClass a typed class instead of using the ? wildcard.
abstract public class AbstractClass<T> {
private final List<T> list;
public AbstractClass(List<T> list) {
this.list = list;
}
public List<T> getList() { return list; }
abstract void addToList(List<T> list);
}
In this case, you would have
public class Class1 extends AbstractClass<Integer> { ... }
public class Class2 extends AbstractClass<String> { ... }

How to convert one generic list to another in java

I am trying to convert one generic list of enums to another generic list type but always getting the following compile error:
Is not applicable for the arguments
private static <T extends Enum<T>> List<T> enumListFromEnumList(List<Object> sourceEnumsList, Class<T> classObject) {
List<T> enums = new ArrayList<T>();
if(sourceEnumsList != null) {
for(Object enumObject : sourceEnumsList) {
if (enumObject instanceof Enum)
enums.add(Enum.valueOf(classObject, enumObject.toString().toUpperCase()));
}
}
return enums;
}
Even I tried this:
private static <T extends Enum<T>> List<T> enumListFromEnumList(List<T> sourceEnumsList, Class<T> classObject) {
List<T> enums = new ArrayList<T>();
if(sourceEnumsList != null) {
for(T enumObject : sourceEnumsList) {
enums.add(Enum.valueOf(classObject, enumObject.toString().toUpperCase()));
}
}
return enums;
}
This is how I'm consuming the function:
adapterInfo.setResponseTypeList( enumListFromEnumList(info.getResponseTypeList(), CAPInfo.ResponseType.class));
Updates:
It works when I convert to this:
private static <S extends Enum<S>, T> List<S> enumListFromEnumList(List<T> sourceEnumsList, Class<S> classObject) {
List<S> enums = new ArrayList<S>();
if(sourceEnumsList != null) {
for(T enumObject : sourceEnumsList) {
enums.add(Enum.valueOf(classObject, enumObject.toString().toUpperCase()));
}
}
return enums;
}
So now the question is how to translate "<S extends Enum<S>, T> List<S>" part ( I mean how it works)
Here is what you are looking for (I think) :
enum EnumA {
FIRST,
SECOND,
THIRD
}
enum EnumB {
FIRST,
SECOND,
THIRD
}
private static <A extends Enum<A>, B extends Enum<B>> List<B> enumListFromEnumList(List<A> sourceEnumsList, Class<B> classObject) {
List<B> enums = new ArrayList<B>();
if (sourceEnumsList != null) {
for (A enumObject : sourceEnumsList) {
enums.add(Enum.valueOf(classObject, enumObject.toString().toUpperCase()));
}
}
return enums;
}
public static void main(String[] args) {
List<EnumA> listA = new ArrayList<EnumA>();
listA.add(EnumA.FIRST);
listA.add(EnumA.THIRD);
List<EnumB> listB = enumListFromEnumList(listA, EnumB.class);
System.out.println(listB);
}

Categories