延迟绑定第二级类型参数

时间:2012-02-17 22:42:46

标签: java generics higher-kinded-types

这是一个人为的复制案例,但请耐心等待。

假设您要为能够将项目添加到不同类型的列表的类创建一个加法器接口,其行为如下:

// 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");

换句话说,我希望界面说:

  

特定列表类型的加法器有一个方法'add'。此方法有两个参数。   第一个是具有T类型的特定类型的列表。   第二个参数是类型为T的项。该方法将项添加到列表中。

我尝试了不同的解决方案:

interface Adder<L extends List<?>> {
    <T> void add(L<T> list, T t);
}

但表达式L<T>在其上下文中是非法的。我得到的错误信息是“类型'L'没有类型参数”。

在add方法的定义之前,我找不到将列表的type参数保持打开的方法。有没有办法指定这个接口,还是需要更高阶的泛型或Java没有的其他东西?

8 个答案:

答案 0 :(得分:4)

不幸的是,Java Generics不支持您正在寻找的功能。您可以获得的最接近的是在方法中需要List,而不是使用类型L.例如:

interface Adder
{
    <T> void add(List<T> list, T t);
}

这不是你想要的,所以下一个最接近的事情是将你的List声明移动到方法体中,但这也是不正确的:

interface Adder
{
    <T, L extends List<T>> void add(List<T> list, T t);
}

问题是你试图将泛型类型分配给任意类型(L),而L可能不是通用的,尽管强制L extends List<?>。没有好的方法可以在编译时或列表类型的运行时强制检查。

答案 1 :(得分:3)

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

我不认为在T定义时可以绑定add,因为必须知道T才能声明L具有类型参数(必须以约束或未约束的形式给予。)

我相信你正在寻找相当于这个C ++ 0x代码:

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

而且我很确定在Java中不可能。

答案 2 :(得分:1)

我不知道这是不是你的意思,但是

public interface Adder<T, L extends List<T>> {

    void add(L list, T t);
}

听起来像一个选项

答案 3 :(得分:1)

这将编译,但它不会强制执行您的规则。

interface Adder<L extends List<?>> {
    <T> void add(L list, T t);
}

您可以执行此操作来强制执行规则

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

答案 4 :(得分:1)

经过多次重写后,这会做你想做的吗?

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

答案 5 :(得分:1)

为了缩短我的答案我用一个类给出了一个例子:

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

答案 6 :(得分:1)

这样做

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

它编译并运行良好。

答案 7 :(得分:0)

您是否考虑过将您的收藏参数拉下来?也就是说,到方法级别。因此,您没有具有通用接口的单独加法器,而只有一个处理所有情况的全局加法器。考虑一下:

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

然后调用Adder,如下所示:

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

您将不再需要(或者能够有意义地使用)加法器的接口,您只需传递/使用单个对象。或者甚至更好,你可以使方法静态并将类用作实用程序类,或者使类成为单例并始终引用一个实例。

完整示例here