Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/399.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Java中是否可以使用部分类型参数应用程序?_Java_Generics_Language Agnostic - Fatal编程技术网

在Java中是否可以使用部分类型参数应用程序?

在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

我知道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 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