我知道Josh Bloch的typesafe heterogeneous container:
<T> void container.put(Class<T>, T);
<T> T container.get(Class<T>);
我也读过关于Neal Gafter的"Super" typesafe hetereogeneneous container:
<T> void container.put(TypeToken<T>, T);
<T> T container.get(TypeToken<T>);
但是,以下类型的容器让我望而却步:
<T> void container.put(Foo<T>, Bar<T>);
<T> Bar<T> container.put(Foo<T>);
其中Foo
和Bar
以及任何通用引用类型。
尝试写出来,你必须做类似的事情:
// Setting T at the class level would force some fixed
// T or at best a bounded range of types
class Container<A, B/*, T */> {
// So we can't have this:
// Map<A<T>, B<T>> map = new HashMap<>();
// And must do this (at the expense of type safety):
Map<A<?>, B<?>> map = new HashMap<>();
<T> void container.put(A<T> a, B<T> b) {
map.put(a, b);
}
<T> B<T> container.get(A<T> a) {
// Not sure what to do here
}
}
然后您就可以执行以下操作:
// This doesn't compile because T is not specified and thus this is not actual Java:
class FooBarContainer extends Container<Foo, Bar, T> {
...
}
FooBarContainer container = new FooBarContainer();
// Can do this:
container.put(new Foo<String>(), new Bar<String>());
Bar<String> bar = container.get( /* some Foo<String> */ );
// Cannot do this:
container.put(new Foo<Long>(), new Bar<String>());
Bar<String> bar = container.get( /* some Foo<Long> */ );
但是,这不是合法的Java。
我在这里看到的是,类级泛型与方法级泛型正交。那是问题吗?如果是这样,有没有办法在Java中协调它?
无论如何,在允许这样的容器的类型系统中你需要什么。所谓的必要功能是什么?如果我必须给它命名,我称之为部分类型参数应用程序,但我确信这个概念必须以一些我不了解的一般形式存在。
答案 0 :(得分:1)
类级别泛型和方法级泛型相结合,它们不是正交的。
我看到一些选项来解决我认为你想要做的事情。但首先,您必须明白,您将无法将泛型声明为另一个泛型的协变,因此您不能说:
class Container<A<B>> {}
因为“A”是一个类型参数,所以没有关联的类型信息,包括它本身是否是泛型类型。如果指定“A”是具有参数的类型,则可以执行此操作,例如:
class Container<A extends Foo<B>> {}
因此,对于第一种情况,您可以使用如下的API:
interface Container<KeyType, ValueType>
{
void put(KeyType k, ValueType v);
ValueType get(KeyType k);
}
允许每个键/值类型的具体子类型(实际上只是java.util.Map),例如:
interface StringContainer extends Container<String, String>
{
void put(String k, String v);
String get(String k);
}
interface FooBarContainer<C> extends Container<Foo<C>, Bar<C>>
{
void put(Foo<C> k, Bar<C> v);
Bar<C> get(Foo<C> k);
}
其中所有键/值对都具有相同的KeyType和ValueType。
在混合中添加方法级泛型允许您使KeyType和ValueType仅与方法调用相关联,而不是与方法的所有调用相关联:
interface VarContainer
{
<VariantType> void put(Foo<VariantType> k, Bar<VariantType> v);
<VariantType> Bar<VariantType> get(Foo<VariantType> k);
}
允许在同一个VarContainer中使用变量键/值对:
VarContainer vc = ...
vc.put(new Foo<String>(), new Bar<String>());
vc.put(new Foo<Long>(), new Bar<Long>());
这将使您能够在具体的子类中指定协变类型:
interface VarContainer<VariantType> extends Container<Foo<VariantType>, Bar<VariantType>>
{
void put(Foo<VariantType> k, Bar<VariantType> v);
Bar<VariantType> get(Foo<VariantType> k);
}
此另一个选项仅在您知道容器中有Foo和Bar时才有效:
interface FooBarVarContainer
{
<VariantType> void put(Foo<VariantType> k, Bar<VariantType> v);
Bar<VariantType> get(Foo<VariantType> k);
}
并且容器现在仅限于Foo / Bar对,但是可以使用相同的变量类型来调用put和两个参数以及在参数和返回值之间获取:
FooBarVarContainer vc = ...
vc.put(new Foo<String>(), new Bar<String>()); // ok
vc.put(new Foo<String>(), new Bar<Long>()); // fails to compile
Bar<Long> = vc.get(new Foo<Long>()); // ok
这有助于解决您的问题所需吗?
还应该提到的是,将可变实例作为键放入映射并不是一种好习惯,因为映射键不仅应该正确实现hashCode和equals方法,而且一旦插入它们,地图就需要根据需要动态重新索引。钥匙改变了。例如,如果hashCode值发生更改,将来在哈希映射中查找该键可能会失败或到达错误的项目。