在Java中是否可以使用部分类型参数应用程序?
我知道Josh Bloch的: 然后你就可以做如下事情:在Java中是否可以使用部分类型参数应用程序?,java,generics,language-agnostic,Java,Generics,Language Agnostic,我知道Josh Bloch的: 然后你就可以做如下事情: // 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 HashM
// 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> */ );
//这不会编译,因为没有指定t,因此这不是实际的Java:
类FooBarContainer扩展了容器{
...
}
FooBarContainer=新的FooBarContainer();
//可以这样做:
container.put(新Foo(),新Bar());
Bar=container.get(/*somefoo*/);
//不能这样做:
container.put(新Foo(),新Bar());
Bar=container.get(/*somefoo*/);
但遗憾的是,这不是合法的
我在这里看到的是类级泛型与方法级泛型是正交的。这就是问题所在吗?如果是这样的话,有没有办法在Java中协调这一点
不管怎样,在允许这样一个容器的类型系统中,您需要什么。必要的特性叫什么?如果必须给它命名的话,我会称之为部分类型参数应用程序,但我确信这个概念一定以某种我不知道的一般形式存在。类级泛型和方法级泛型结合在一起,它们不是正交的 我看到了一些选项来解决我认为你正在试图做的事情。但首先,您必须了解,您不能将一个泛型声明为另一个泛型的协变,因此您不能说:
class Container<A<B>> {}
类容器{}
因为“A”是一个类型参数,因此没有关联的类型信息,包括它本身是否是泛型类型。如果指定“A”是具有参数的类型,则可以执行该操作,例如:
class Container<A extends Foo<B>> {}
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);
}
类容器{}
因此,对于第一种情况,您可以使用如下API:
interface Container<KeyType, ValueType>
{
void put(KeyType k, ValueType v);
ValueType get(KeyType k);
}
接口容器
{
无效认沽权(关键类型k,价值类型v);
ValueType get(键类型k);
}
这将允许每个键/值类型使用具体的子类型(实际上只是一个java.util.Map),例如:
class Container<A extends Foo<B>> {}
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);
}
接口StringContainer扩展容器
{
无效认沽权(第k串、第v串);
字符串get(字符串k);
}
FooBarContainer扩展容器的接口
{
无效认沽权(福岛、巴五);
巴吉(福);;
}
其中,所有键/值对都具有相同的键类型和值类型
将方法级泛型添加到混合中可以使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>());
VarContainer vc=。。。
put(newfoo(),newbar());
put(newfoo(),newbar());
这将使您能够在具体的子类中指定协变类型:
interface VarContainer<VariantType> extends Container<Foo<VariantType>, Bar<VariantType>>
{
void put(Foo<VariantType> k, Bar<VariantType> v);
Bar<VariantType> get(Foo<VariantType> k);
}
接口VarContainer扩展容器
{
无效认沽权(福岛、巴五);
巴吉(福);;
}
仅当您知道容器中有Foo和Bar时,此其他选项才有效:
interface FooBarVarContainer
{
<VariantType> void put(Foo<VariantType> k, Bar<VariantType> v);
Bar<VariantType> get(Foo<VariantType> k);
}
接口FooBarVarContainer
{
无效认沽权(福岛、巴五);
巴吉(福);;
}
容器现在仅限于Foo/Bar对,但可以使用相同的变量类型调用参数和返回值之间的put和get:
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
FooBarVarContainer vc=。。。
vc.put(新Foo(),新Bar());//好啊
vc.put(新Foo(),新Bar());//未能编译
Bar=vc.get(new Foo());//好啊
这有助于你解决问题吗
还应该提到的是,将可变实例作为键放入映射中并不是一个好的做法,因为映射键不仅应该正确实现hashCode和equals方法,而且一旦插入它们,如果键发生更改,映射还需要根据需要动态地重新编制索引。例如,如果hashCode值更改,将来在hashmap中查找该键可能会失败或到达错误的项。问题有点不清楚,如果您有一个带有
a
和B
类型见证的类,如果您将List
(例如)作为类型参数传递,则a
将是List
,不仅仅是原始的列表
。除非我完全忽略了这个问题问:如果你已经知道A是一个TypeToken或一个类,那么为什么要让A成为Container
类的通用参数?@Federicoperalthaffner:A
可以是TypeToken
,Class
或Foo
,只要它是TypeToken
,Class
,或者Foo
是map
键。@dimadima理解。泛型不能用作A。A必须是已知类型,如TypeToken或Foo,或者是已知类型的祖先或后代的类型。@Rogue yes您可以对A
执行List
操作,对B
执行Foo
操作,但是您不能在方法中进一步参数化t
。你会被字符串卡住
。我想说“这个容器包含List
和Foo
的映射,其中T
是一个不变量,当您放置(A,B)时
,A
和B
的通用类型必须匹配。这太好了。是的,谢谢。我不知道为什么我没有这样想。下次机会出现时,我会玩这个。
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