michelson
michelson

Reputation: 750

Java generics - map function to a list


I'm kind of new to generics in Java and I've faced such a problem: Let's say you have your own List implementation and you want to provide a mechanism to simultaneously convert all elements using some kind of mapping (functional interface) and collect them into a new list.

While the idea and use of functional interface (IMapper in my case) is straightforwad I can't quite think of what signature a function performing mapping should have?

Here's a little use case example and what I thought of as well. It does not work unfortunately and I guess the main problem is: How the second V param type should be passed in such case?

public interface IMapper<T,V> { V map(T v); }

public class MyList<T> extends ArrayList<T> {
  public MyList<V> map(IMapper <T,V> mapper) {
    MyList<V> list = new MyList<>();
    for(T v : this) {
      list.add(mapper.map(v));
    }
    return list;
  }
}
// in main
MyList<Integer> list1 = new MyList<>(); 
// fill etc..
IMapper<Integer,String> m = (i) -> { return i.toString(); };
// "Change" list
MyList<String> list2 = list1.map(m);

PS: I think that such thing is most probably already implemented in Java (stream() and what follows I guess?) however it suppose to be exercise for me. Any tip would be much appreciated :)

Upvotes: 3

Views: 1668

Answers (2)

Transient
Transient

Reputation: 326

You can add the map result type to you function definition as following:

class MyList<T> extends ArrayList<T> {

public <V> MyList<V> map(IMapper<T, V> mapper) {
    MyList<V> list = new MyList<>();
    for (T v : this) {
        list.add(mapper.map(v));
    }
    return list;
}

}

Example:

MyList<Integer> number = new MyList<>();

number.add(1);
number.add(2);
number.add(3);

number.map(v -> "#" + v).forEach(System.out::println);

And you can have the same result using Java8 streams as following:

List<Integer> numberStream = new ArrayList<>();
numberStream.add(1);
numberStream.add(2);
numberStream.add(3);
numberStream.stream().map(v -> "#" + v).forEach(System.out::println);

Upvotes: 2

marstran
marstran

Reputation: 27971

You can define the generic parameter as a type parameter on your map method. Like this:

public <V> MyList<V> map(IMapper <T,V> mapper) {
    ...
}

Type parameters can be defined in two ways, either on a class or on a method. If it's defined on a class, it can be used throughout the class. If it's defined on a method, it can only be used in that method.

In your case, the T parameter is defined on the class, while the V parameter can be defined on the method.

Upvotes: 2

Related Questions