Java generics: How to encode a Functor interface in Java? - java

I want to define a Functor class in Java. This works:
//a Function
public interface F<A,R> {
public R apply(A a);
}
public interface Functor<A> {
public <B> Functor<B> fmap(F<A,B> f);
}
However the return value of fmap should be not Functor, but the appropriate subclass. Usually this can be encoded with the CRTP, but here I seem to hit a wall because of the additional parameter A. E.g. the following and similar encodings don't work ("type parameter FInst is not within its bounds"):
public interface Functor<A, FInst extends Functor<A,FInst>> {
public <B, I extends Functor<B,FInst>> I fmap(F<A,B> f);
}
[Clarification]
With "appropriate subclass" I mean the type of the class being called itself. E.g. Lists are functors, so I would like to write something like
public class ListFunctor<A> implements ??? {
final private List<A> list;
public ListFunctor(List<A> list) {
this.list = list;
}
#Override
<B> ListFunctor<B> fmap(F<A,B> f) {
List<B> result = new ArrayList<B>();
for(A a: list) result.add(f.apply(a));
return new ListFunctor<B>(result);
}
}
I'm aware that I could write this even with the first definition I gave (because covariant return types are allowed), but I want that the return type "ListFunctor" is enforced by the type system (so that I can't return a FooFunctor instead), which means that the Functor interface needs to return the "self-type" (at least it is called so in other languages).
[Result]
So it seems what I want is impossible. Here is a related blog-post: http://blog.tmorris.net/higher-order-polymorphism-for-pseudo-java/
[Aftermath]
I stumbled over this age-old question of mine, and realized that this was the starting point of the amazing journey with my library highJ, containing much more than a simple Functor. I would have never imagine that people would use this crazy stuff for anything serious, but it happened, and that makes me very happy.

public interface Functor<A, FInst extends Functor<A,FInst>> {
public <B, I extends Functor<B,FInst>> I fmap(F<A,B> f);
}
This code generates an error because when you define I, you define it to be a subclass of Functor<B,FInst>, but the FInst parameter must be a subclass of Functor<B,FInst> in this case, while it is defined above as being a subclass of Functor<A,FInst>. Since Functor<A,FInst> and Functor<B,FInst> aren't compatible, you get this error.
I haven't been able to solve this completely, but I could do at least a half of the job:
import java.util.ArrayList;
import java.util.List;
interface F<A,R> {
public R apply(A a);
}
interface Functor<A, FClass extends Functor<?, FClass>> {
public <B> FClass fmap(F<A,B> f);
}
public class ListFunctor<A> implements Functor<A, ListFunctor<?>> {
final private List<A> list;
public ListFunctor(List<A> list) {
this.list = list;
}
#Override
public <B> ListFunctor<B> fmap(F<A,B> f) {
List<B> result = new ArrayList<B>();
for(A a: list) result.add(f.apply(a));
return new ListFunctor<B>(result);
}
}
This works, and it properly limits the set of allowed return types to ListFunctor, but it doesn't limit it to subclasses of ListFunctor<B> only. You could declare it as returning ListFunctor<A> or any other ListFunctor, and it would still compile. But you can't declare it as returning a FooFunctor or any other Functor.
The main problem with solving the rest of the problem is that you can't limit FClass to subclasses of ListFunctor<B> only, as the B parameter is declared at the method level, not at the class level, so you can't write
public class ListFunctor<A> implements Functor<A, ListFunctor<B>> {
because B doesn't mean anything at that point. I couldn't get it working with the second parameter to the fmap() either, but even if I could, it would just force you to specify the return type twice - once in the type parameter and once more as the return type itself.

Looking from a different angle, it seems Functor shouldn't be modeled as a "Wrapper" around the data, but actually more like a type-class, which works on the data. This shift of perspective allows to encode everything without a single cast, and absolutely type-safe (but still with a lot of boilerplate):
public interface Functor<A, B, FromInstance, ToInstance> {
public ToInstance fmap(FromInstance instance, F<A,B> f);
}
public class ListFunctor<A,B> implements Functor<A, B, List<A>, List<B>> {
#Override
public List<B> fmap(List<A> instance, F<A, B> f) {
List<B> result = new ArrayList<B>();
for(A a: instance) result.add(f.apply(a));
return result;
}
}
List<String> stringList = Arrays.asList("one","two","three");
ListFunctor<String,Integer> functor = new ListFunctor<String,Integer>();
List<Integer> intList = functor.fmap(stringList, stringLengthF);
System.out.println(intList);
//--> [3, 3, 5]
It seems I was too focused on packing both FromInstance and ToInstance in one type parameter (e.g. List in ListFunctor), which isn't strictly necessary. However, it's a heavy burden to have now not only A but also B as type parameter, which may make this approach practically unusable.
[Research]
I found a way to make this version at least a little bit useful: This functor can be used to lift a function. E.g. if you have F<String, Integer>, you can construct a F<Foo<String>, Foo<Integer>> from it when you have a FooFunctor defined as shown above:
public interface F<A,B> {
public B apply(A a);
public <FromInstance, ToInstance> F<FromInstance, ToInstance> lift(
Functor<A,B,FromInstance, ToInstance> functor);
}
public abstract class AbstractF<A,B> implements F<A,B> {
#Override
public abstract B apply(A a);
#Override
public <FromInstance, ToInstance> F<FromInstance, ToInstance> lift(
final Functor<A, B, FromInstance, ToInstance> functor) {
return new AbstractF<FromInstance, ToInstance>() {
#Override
public ToInstance apply(FromInstance fromInstance) {
return functor.fmap(fromInstance, AbstractF.this);
}
};
}
}
public interface Functor<A, B, FromInstance, ToInstance> {
public ToInstance fmap(FromInstance instance, F<A,B> f);
}
public class ListFunctor<A, B> implements Functor<A, B, List<A>, List<B>> {
#Override
public List<B> fmap(List<A> instance, F<A, B> f) {
List<B> result = new ArrayList<B>();
for (A a : instance) {
result.add(f.apply(a));
}
return result;
}
}
//Usage:
F<String, Integer> strLenF = new AbstractF<String, Integer>() {
public Integer apply(String a) {
return a.length();
}
};
//Whoa, magick!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
F<List<String>,List<Integer>> liftedF = strLenF.lift(new ListFunctor<String, Integer>());
List<String> stringList = Arrays.asList("one", "two", "three");
List<Integer> intList = liftedF.apply(stringList);
System.out.println(intList);
//--> [3, 3, 5]
I think it's still not very useful, but at least way cooler than the other attempts :-P

Building on the answer of Sergey, I think I came close to what I wanted. Seems like I can combine his idea with my failed attempt:
public interface Functor<A, Instance extends Functor<?, Instance>> {
public <B, I extends Functor<B,Instance>> I fmap(F<A,B> f);
}
public class ListFunctor<A> implements Functor<A, ListFunctor<?>> {
final private List<A> list;
public ListFunctor(List<A> list) {
this.list = list;
}
#Override
public <B, I extends Functor<B, ListFunctor<?>>> I fmap(F<A,B> f) {
List<B> result = new ArrayList<B>();
for(A a: list) result.add(f.apply(a));
return (I) new ListFunctor<B>(result);
}
}
List<String> list = java.util.Arrays.asList("one","two","three");
ListFunctor<String> fs = new ListFunctor<String>(list);
ListFunctor<Integer> fi = fs.<Integer,ListFunctor<Integer>>fmap(stringLengthF);
//--> [3,3,5]
The remaining problem is that I could write e.g. ListFunctor<StringBuilder> fi = fs.<Integer,ListFunctor<StringBuilder>> without complaints from the compiler. At least I can look for a way to hide the ugly guts behind a static method, and to enforce that relation behind the scenes...

Does anyone still use Java and ponder this problem? You might find this useful...
I've been pondering this for a looooong time. I believe I've made something satisfactory. What I would really like to is indeeed impossible in Java.
This is ideal:
interface Functor<T, CONCRETE<A> extends Functor<A, CONCRETE>> {
CONCRETE<U> fmap(Func<T, U>);
}
Unfortunately, this is make-believe syntax. This kind of thing is possible in C++ with template-template parameters, but not Java.
I was tempted to write this simple thing:
interface Functor<T> {
Functor<U> fmap(Func<T, U>);
}
This works in some cases, because an implementation can return a covariant return type (for example, List could return a List from this function), but it breaks down when you try passing around generic variables of type "F extends Functor", or a subclass of Functor, etc...
What I ended up doing was introduce a "dummy type variable", like so:
interface Functor<CONCRETE, T> {
Functor<CONCRETE, U> fmap(Func<T, U>);
}
The "concrete type" should be the type itself, or some dummy type that guarantees the uniqueness of its implementors. Here's an example implementation:
public final class Array<T> implements Functor<Array<?>, T> {
private final T[] _values;
#SafeVarargs
public Array(T... values) {
_values = values;
}
#SuppressWarnings("unchecked")
#Override
public <A, RESULT extends Functor<Array<?>, A>> RESULT fmap(Function<T, A> f) {
A[] result = (A[]) new Object[_values.length];
for (int i = 0; i < _values.length; ++i) {
result[i] = f.apply(_values[i]);
}
return (RESULT) new Array<A>(result);
}
}
The cast to (RESULT) is safe because there can only be one type that matches "Functor, T>", and that's "Array". The disadvantage of this, is that generic code may need to pass around this "CONCRETE" type in a bunch of places, and it makes your signatures unwieldy. For instance:
public class Test {
public static <CONCRETE, FInt extends Functor<CONCRETE, Integer>, FBool extends Functor<CONCRETE, Boolean>> FBool intToBool(FInt ints) {
return ints.fmap(x -> x > 5);
}
public static void main() {
Array<Integer> ints = new Array<>();
Array<Boolean> bools1 = ints.fmap(x -> x > 5); // this works because Array<> implements fmap covariantly
Array<Boolean> bools2 = intToBool(ints); // but this also works thanks to our dummy CONCRETE type
}
}

I think you want to do something that makes no sense (type wise).
interface Getter<Type> {
Type get();
}
If your application wants a getter that returns Integers, don't give it one that returns Objects.
If you don't know if it will return Objects or Integers you are trying to do something the wrong way.
If YOU KNOW it will return Integers, then wrap the getter so that it casts to integers.
Hope this is what you are looking for .
EDIT:
Explanation of why (I think) this can not be done.
Objects have there types set when you use new.
Take each type and replace it with a letter.
Take any number of another objects and do the same.
What letter do you want your function to return?
If the answer is that you want a mix, well then its too late. Types are decided at new, and you are already past new.

Related

Using self-referential generic types in Java

Consider the following Java method:
<T extends List<T>> List<T> getMyList() {
return Collections.emptyList();
}
I can assign its output to a variable with a raw type, like so:
List x = getMyList();
List<List> y = getMyList();
But, I can't think of any way to assign its output to a fully parameterized type. In particular, I can't think of a non-raw, concrete type T that would satisfy List<T> z = getMyList();
Can we create such a T ?
If not, why not?
For context, I created this question while trying to understand how Enums are implemented in Java.
Here's an example of a concrete type that both works and starts to hint at a possible use-case (registration of some sort). The type consists acts like both an instance of some type, and as a container for all instances of that type.
public class WeirdEnum extends AbstractList<WeirdEnum> {
private static List<WeirdEnum> underlyingList = new ArrayList<>();
#Override
public WeirdEnum get(int index) { return underlyingList.get(index); }
#Override
public int size() { return underlyingList.size(); }
static <T extends List<T>> List<T> getAList() {
return Collections.emptyList();
}
public WeirdEnum() {
underlyingList.add(this); // Sufficient for our example but not a good idea due to concurrency concerns.
}
static List<WeirdEnum> foo = WeirdEnum.getAList();
}
Not sure if I fully understand your question, but here's an example:
class Example<T> implements List<Example<T>> {
...
}
...
List<Example<String>> list = getMyList();
Every enum in Java extends from the base-enum-class Enum<T extends Enum<T>>, where T is the actual type of the implementing enum.
When writing SomeClass<T extends SomeClass<T>> you can enforce that the type-parameter is always the implementing class itself.
Let's say you have this interface:
public interface MyInterface<T extends MyInterface<T>> {
T getSelf();
}
And this implementing class:
public class MyClass implements MyInterface<MyClass> {
public MyClass getSelf() {
return this;
}
}
In MyClass it is not possible to use any other type-parameter than MyClass itself.

Issue with class cast exception between subclasses and collections in Java

So, I have the following two classes:
Class A { }
Class B extends A { }
Class C extends A { }
And the following method:
public void foo(Collection<A> bar) {
List<A> listA = new ArrayList<>();
for(A a : bar) {
//a and anotherA are both of the same subtype of A
A anotherA = getAnotherA(a);
listA.add(anotherA);
}
bar.clear();
bar.addAll(listA);
}
Now, I am trying to call this method two separate ways, but I cannot get the casting to work properly... Hopefully I am just overlooking something small.
So, here are the two ways I am calling it:
Way 1:
A obj = ...;
Field field = //get field representing a collection of sub-type of A
Collection<A> collection = field.get(...);
foo(collection);
way 2:
B obj = ...;
Set<C> setOfC = b.getSetOfC();
foo(setOfC);
I have tried numerous casting attempts, but I cannot seem to get it to compile! For instance, in way 2, I tried casting setOfC to Set<A>, but I get a class cast exception. I have tried to cast bar to Collection<? extends A>, but then bar.addAll(..) fails. I have tried to add a generic to foo, but also get errors. In way 1, I have also tried to cast collection to Collection<? extends A>, but still not luck.
You cannot pass a Set<C> into a method expecting a Collection<A>, even though a Set is a Collection, and even though a C is an A, because Java's generics are invariant.
You can introduce a type parameter on the foo method, with an A upper bound. Then you can use the type parameter throughout the method. This will ensure that the same subtype of A is used.
public static <T extends A> void foo(Collection<T> bar) {
List<T> listA = new ArrayList<>();
for(T a : bar) {
//a and anotherA are both of the same subtype of A
T anotherA = getAnotherA(a);
listA.add(anotherA);
}
bar.clear();
bar.addAll(listA);
}
Your comment seems to indicate that a and anotherA are of the same type, so this should compile for you. If not, then the getAnotherA method will need some work so that passing in a C will return a C and not an A.
if you have this classes:
Class YourA { }
Class YourB extends YourA { }
Class YourC extends YourA { }
your method's signature most be like
public <G extends YourA> void foo(Collection<G> bar) {...}

Safe workaround for broken contravariant bounds in Java?

As discussed in Bounding generics with 'super' keyword the Java type system is broken/incomplete when it comes to lower bounds on method generics. Since Optional is now part of the JDK, I'm starting to use it more and the problems that Guava encountered with their Optional are starting to become a pain for me. I came up with a decent work around, but I'm not sure its safe. First, let me setup the example:
public class A {}
public class B extends A {}
I would like to be able to declare a method like:
public class Cache {
private final Map<String, B> cache;
public <T super B> Optional<T> find(String s) {
return Optional<T>.ofNullable(cache.get(s));
}
}
So that both of the following work:
A a = cache.find("A").orElse(new A())
B b = cache.find("B").orElse(new B())
As a workaround, I have a static utility method as follows:
public static <S, T extends S> Optional<S> convertOptional(Optional<T> optional) {
return (Optional<S>)optional;
}
So my final question, is this as type-safe as the 'ideal' code above?
A a = OptionalUtil.<A,B>convertOptional(cache.find("A")).orElse(new A());
You're effectively trying to view the Optional<B> returned as an Optional<A> without changing the return type (since you can't use the super). I would just map the identity function.
A a = cache.find("A").map(Function.<A> identity()).orElse(new A());
// or shorter
A a = cache.find("A").<A> map(x -> x).orElse(new A());
I don't see anything wrong with your approach though.
Yes, your code is type safe because you are casting Optional<T> to Optional<S>, and T is always an S. You can indicate this to the compiler by using the #SuppressWarnings("unchecked") annotation on your convertOptional utility method.
As well as the other excellent answer, you could do it this way. Note the lack of generics compared to your first version.
package com.company;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
public class Cache {
private static final Function<A,A> CAST_TO_A = Function.<A>identity();
private final Map<String, B> cache = new HashMap<>();
public Optional<B> find(String s) {
return Optional.ofNullable(cache.get(s));
}
public static void main(String[] args) {
Cache cache = new Cache();
Optional<B> potentialA = cache.find("A");
A a = potentialA.isPresent() ? potentialA.get() : new A();
A anotherWay = cache.find("A").map(CAST_TO_A).orElse(new A());
B b = cache.find("B").orElse(new B());
}
public static class A {}
public static class B extends A {}
}
In practice, I think it is type-safe, because the Optional class is immutable and T is subtype of S.
Beware though that T is subtype of S does NOT mean that Optional<T> is subtype of Optional<S>, so theoretically that cast is not correct. And in some cases, it is not safe to do such kind of casts and it can be problematic at run-time (as it happens with List).
So my suggestion is to avoid casts whenever we can, and I would rather define a method like the getOrElse. Also, for the sake of completeness, the generic types in your method convertOptional could be simplified as below.
class OptionalUtil {
public static <S> S getOrElse(Optional<? extends S> optional, Supplier<? extends S> other) {
return optional.map(Function.<S>identity()).orElseGet(other);
}
public static <S> Optional<S> convertOptional(Optional<? extends S> optional) {
return (Optional<S>)optional;
}
}
And they could be use like that:
A a1 = OptionalUtil.getOrElse(cache.find("A"), A::new);
A a2 = OptionalUtil.<A>convertOptional(cache.find("A")).orElseGet(A::new);
[EDIT] I replaced the method orElse by orElseGet, because with the former a new A object will be created even if the Optional is present.

Method with two generic collections of same class

I would like to implement the following method, but I'm uncertain how to do this in Java generics.
This is what I would like to have:
public <C extends Collection> C<DomainDTO> transform(C<DomainModel> arg);
This is what compiles, but doesn't ensure A and B are of the same class that extends Collection:
public <A extends Collection<DomainDTO>, B extends Collection<DomainModel>> A transform(B arg);
Thanks for any ideas, or just confirmation that it's impossible.
EDIT:
I'll try to word this better, since it seems people confuse what I'm looking for. The use case here is (should be) simple. I need a method to populate a Collection of DTOs from a Collection of models. Right now I have many methods for different Collections, but I would prefer to have one generic method that takes any Collection as the parameter, and returns the DTOs in a Collection of the same class.
Specify the type of the collection parameter, not the collection itself:
public <T> Collection<T> transform(Collection<T> arg);
If the question is asking for both the element type and the collection type to be the same:
public <T, C extends Collection<T>> C transform(C arg);
EDIT: The question originally was ambiguous, to say the least. My proposed solution is not applicable to the edited question. However, the recommendation I made is still valid:
I wonder how you would like to implement this without using newInstance etc. You have to create a new instance of an unknown type...
You might alternatively consider a signature [...] where the caller can pass the target collection manually, and type safety is still maintained
EDIT: Updated code for the updated question:
The most generic form that I could imagine for this is sketched below.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class SomeCollectionTest
{
static interface Converter<S, T>
{
T convert(S s);
}
static class Parser implements Converter<String, Integer>
{
#Override
public Integer convert(String s)
{
return Integer.parseInt(s);
}
}
public static void main(String[] args)
{
Collection<String> input = Arrays.asList("0","1","2","3");
List<String> listIn = new ArrayList<String>(input);
Set<String> setIn = new HashSet<String>(input);
Parser p = new Parser();
List<Integer> listOutA = convert(listIn, new ArrayList<Integer>(), p);
List<Integer> listOutB = convert(setIn, new ArrayList<Integer>(), p);
//List<Integer> noListOut = convert(listIn, new HashSet<Integer>(), p);
Set<Integer> setOutA = convert(setIn, new HashSet<Integer>(), p);
Set<Integer> setOutB = convert(setIn, new HashSet<Integer>(), p);
//Set<Integer> noSetOut = convert(listIn, new ArrayList<Integer>(), p);
System.out.println(listOutA);
System.out.println(setOutA);
}
private static <S, T, C extends Collection<? super T>> C convert(
Iterable<? extends S> input, C output, Converter<S, T> converter)
{
for (S s : input)
{
output.add(converter.convert(s));
}
return output;
}
}
And by the way: I'd even consider it as being advantageous to have the possibility to convert, for example, a Collection<DomainDTO> into a List<DomainModel> - particularly when you don't know the exact type of the first collection. Otherwise, you'd have to do this in two steps:
Collection<DTO> collectionOfDto = read();
Collection<DomainModel> dm = convert(dto); // Only receive a collection here...
// Sigh, I need a list. Maybe it already IS a list, but
// I don't know it. So I have to do this:
List<DomainModel> list = new ArrayList<DomainModel>(dm);

Delay binding of second-level type parameter

This is a bit of a contrived reproducing case, but bear with me.
Suppose you want to create an adder interface for classes capable of adding items to different types of lists with the following behavior:
// Can add items to any type of array list.
Adder<ArrayList> arrayListAdder = ...;
// Ok. Right list type and item types match.
arrayListAdder.add(new ArrayList<String>(), "test");
// Ok. Right list type and item types match.
arrayListAdder.add(new ArrayList<Integer>(), 3);
// Compile error. Item types do not match.
arrayListAdder.add(new ArrayList<Integer>(), "test");
// Compile error. Wrong list type although item types match.
arrayListAdder.add(new LinkedList<String>(), "test");
In other words, I want the interface to say:
An adder for an specific list type has a method 'add'. This method takes two arguments.
The first is a list of this specific type with items of type T.
The second argument is an item of type T. The method adds the item to the list.
I tried with different solutions along the lines of:
interface Adder<L extends List<?>> {
<T> void add(L<T> list, T t);
}
But the expression L<T> is illegal in its context. The error message I get is "Type 'L' does not have type parameters".
I cannot find a way of leaving the type parameter of the list open until the definition of the add method. Is there any way of specifying this interface, or does that require higher-order generics or something else which Java doesn't have?
Unfortunately, Java Generics do not support the functionality you are looking for. The closest you can get is to require a List in the method, and not use type L. Such as:
interface Adder
{
<T> void add(List<T> list, T t);
}
This isn't what you are looking for though, so the next closest thing would be to move your List declaration into the method body, however this is also incorrect:
interface Adder
{
<T, L extends List<T>> void add(List<T> list, T t);
}
The problem is you are attempting to assign a generic type to an arbitrary type (L), while L may not be genericized, despite forcing L extends List<?>. There is no good way to force the check at compile time or at run time of the list type.
interface Adder<T, L extends List<T>> {
void add(L list, T t);
}
class ArrayListAdder implements Adder<String, ArrayList<String>> {
#Override
public void add(ArrayList<String> list, String t) {
list.add(t);
}
}
I don't think binding T is possible at add definition time, since T must be known in order to declare L having a type parameter (it must be given in either bound or unbound form).
I believe you are looking for the equivalent of this C++0x code:
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
template <template <typename _ElementT, typename _AllocatorT> class CollectionT>
struct Adder {
template <typename ElementT, typename AllocatorT>
void add(CollectionT<ElementT, AllocatorT> &collection, ElementT element);
};
struct VectorAdder : public Adder<std::vector> {
template <typename ElementT, typename _Alloc>
void add(std::vector<ElementT, _Alloc> &vector, ElementT element) {
vector.push_back(element);
}
};
int main() {
std::vector<int> vi;
vi.push_back(1);
std::vector<double> vd;
vd.push_back(1.1);
VectorAdder va;
va.add(vi, 2); // instantiates VectorAdder::add<int, ...>
va.add(vd, 2.2); // instantiates VectorAdder::add<double, ...>
for_each(vi.begin(), vi.end(), [](int x) { std::cout << x << ' '; });
for_each(vd.begin(), vd.end(), [](double x) { std::cout << x << ' '; });
return 0;
}
And I'm pretty sure that's not possible in Java.
I do not know if this is what you mean but
public interface Adder<T, L extends List<T>> {
void add(L list, T t);
}
sounds like an option
This will compile but it does not enforce your rules.
interface Adder<L extends List<?>> {
<T> void add(L list, T t);
}
You can do this to enforce your rules
interface Adder<L extends List<T>, T> {
void add(L list, T t);
}
class foo implements Adder<List<Integer>, Integer> {
public void add(List<Integer> list, Integer t) {
...
to shorten my answer i gave an example using a class:
class Adder<T, L extends List<T>> {
void add(L list, T t) { /* your logic */}
void test() {
new Adder<Integer, ArrayList<Integer>>().add(new ArrayList<Integer>(), new Integer(1));
}
}
Will this do
import java.util.List;
import java.util.ArrayList;
interface Adder<K> {
void add(List<K> l, K k1);
};
class IntegerListAdder implements Adder<Integer> {
public void add(List<Integer> l, Integer i) {
l.add(i);
}
}
class StringListAdder implements Adder<String> {
public void add(List<String> l, String i) {
l.add(i);
}
}
public class AdderTest {
public static void main(String... argv) {
IntegerListAdder ila = new IntegerListAdder();
List<Integer> l = new ArrayList<Integer>();
ila.add(l,1);
ila.add(l,2);
System.out.println(l);
StringListAdder sla = new StringListAdder();
List<String> s = new ArrayList<String>();
sla.add(s,"One");
sla.add(s,"Two");
System.out.println(s);
}
}
It compiles and runs fine.
After many rewrites, does this do what you want?
interface Adder<L extends Collection<S>, S> {
public S add(L c, S s);
}
class AdderImpl <L extends Collection<S>, S> implements Adder<L, S> {
public S add(L c, S s) {
c.add(s);
return s;
}
}
public void test() {
Adder<List<String>, String> listAdder = new AdderImpl<List<String>, String>();
Adder<Set<String>, String> setAdder = new AdderImpl<Set<String>, String>();
listAdder.add(new ArrayList<String>(), "Hello");
// setAdder.add(new ArrayList<String>(), "Hello"); Complier error - can't use List on SetAdder
setAdder.add(new HashSet<String>(), "Hello");
}
Have you considered pulling your collection parameter down? That is, to the method level. So that you do not have separate adders with a common interface, but just one, global adder that handles all cases. Consider this:
class Adder {
public <T, W extends Wrapped<T>> void add(ArrayList<W> list, W w) {
System.out.println("list.add " + w);
w.commonWrappedMethod();
list.add(w);
}
public <T, W extends Wrapped<T>> void add(HashSet<W> set, W w) {
System.out.println("set.add " + w);
w.commonWrappedMethod();
set.add(w);
}
// For all other collections
public <T, W extends Wrapped<T>> void add(Collection<W> col, W w) {
System.out.println("col.add " + w);
w.commonWrappedMethod();
col.add(w);
}
}
The Adder is then invoked like this:
ArrayList<Wrapped1<Integer>> w1il = new ArrayList<Wrapped1<Integer>>();
HashSet<Wrapped1<String>> w1ss = new HashSet<Wrapped1<String>>();
Vector<Wrapped2<String>> w2sv = new Vector<Wrapped2<String>>();
Adder adder = new Adder();
adder.add(w1il, new Wrapped1<Integer>(1));
adder.add(w1ss, new Wrapped1<String>("six"));
adder.add(w2sv, new Wrapped2<String>("twelve"));
You would no longer need (or be able to employ meaningfully, in fact) an interface for the adder, you would just pass around/use the single object. Or even better, you would make the methods static and use the class as a utility class, or make the class a singleton and always refer to the one instance.
Full example is available here.

Categories

Resources