[英]How to avoid Generics in this case?
class Attribute<T>{
private T attr;
public Attribute(T attr){
this.attr = attr;
}
}
Class Matrix<T>{
private String name;
List<Attribute<T>> list;
public Matrix(String name, T t){
this.name = name;
list = new ArrayList<>();
list.add(new Attribute<T>(t));
}
}
interface Extractor<T> {
public List<Matrix<T>> extract();
}
InfoExtractor implements Extractor<String>{
public List<Matrix<String>> extract(){
List<Matrix<String>> matrixList = new ArrayList<>();
// The problem is here!!!!
matrixList.add(new Matrix<String>("abc"));
}
}
Document<T>{
Map<String, List<Matrix<T>>> matrixMap;
public void process(){
...
Extractor<T> extractor = (Extractor<T>) new StringExtractor(sent);
List<Matrix<T>> matrix = extractor.extract(...);
}
我的问题是,有没有办法避免将Matrix定义为泛型类型? 我想避免的原因是“ List<Attribute<T>>
”在多个其他类中使用,可以是私有成员变量,也可以是方法返回类型。 由于属性,似乎我必须将一些其他相关类定义为泛型类型,这会导致我的问题。
在这种情况下,有没有办法不将Matrix定义为泛型,但将“list”变量保持为泛型类型?
您的问题不在于通用实现,而在于其用法:
class InfoExtractor implements Extractor{
// The problem is actually here
public <T> List<Matrix<T>> extract(){
List<Matrix<T>> matrixList = new ArrayList<>(); //and here
// "The problem is here!!!!"
matrixList.add(new Matrix<String>("abc"));
}
}
<T>
表示您正在绑定相对于方法调用的新泛型类型。 简而言之,一个新的T
类型只是为了这个方法的执行。 您还可以创建List<Matrix<T>>
,然后尝试添加新的Matrix<String>
。 如果您知道列表将是Matrix<String>
类型,那么您可以在InfoExtractor
指定:
//If you cannot generify the interface for some reason
interface Extractor {
public List<? extends Matrix<?>> extract(); //explained at bottom
}
//IDEALLY, then implement Extractor<String> instead
interface Extractor<T> {
public List<Matrix<T>> extract();
}
class InfoExtractor implements Extractor { //or Extractor<String>
public List<Matrix<String>> extract() {
List<Matrix<String>> matrixList = new ArrayList<>();
matrixList.add(new Matrix<>("abc"));
return matrixList;
}
}
当然,您可以看到Extractor
的方法签名已更改。 由于在返回类型上使用嵌套泛型,在编译时类型匹配会让事情变得有些混乱。 Matrix的<?>
是相当不言自明的,我们在Matrix内部返回多个且可能未知的类型。
通过指定? extends Matrix
在Extractor
上? extends Matrix
,我们指定了正确的方差。 具体的泛型是不变的 ,因此List<Toyota>
不是List<Car>
,即使Toyota
是Car
的子类。 如果我们希望Matrix
是协变的 ,那么我们不幸地需要Matrix上的边界。 这实际上意味着,忽略Liskov的替换主体,我们基本上是引用提取器的具体子类( InfoExtractor ex
vs Extractor ex
),特别是从可用性的角度来看(因为具体类可以返回正确的类型安全,而接口不能) 。
当您将列表中的矩阵的<T>
指定为类级泛型类型时,这当然会得到更加清晰/干净的处理。
这取决于你想做什么。
如果您打算使用Matrix来保存单一类型的属性,那就是T,那么最好的方法似乎是您发布的编译时间类型检查。
如果从Matrix中删除泛型类型T,则无法知道每个属性的类型。 您可以从Matrix声明中完全删除T,并使用通配符作为Attribute变量。 虽然这将允许您在同一Matrix对象中存储不同T类型的属性,但它将删除编译类型检查。 换句话说,使用Matrix的类必须知道期望什么类,并且可能将每个属性转换为期望的类。
Class Matrix{
private String name;
List<Attribute<?>> list;
public Matrix(String name, T t){
this.name = name;
list = new ArrayList<>();
list.add(new Attribute<>(t));
}
}
添加Class变量并将Class对象保存在属性中可以帮助您在运行时保留一些信息以标识属性内对象的类。
class Attribute<T>{
private T attr;
private final Class<T> clazz;
public Attribute(T attr, Class<T> clazz){
this.attr = attr;
this.clazz=clazz;
}
public Attribute(T attr){
this.attr = attr;
Objects.requireNonNull(attr);
this.clazz= (Class<T>) attr.getClass();
}
}
使用此方法,原始构造函数只能用于非null值,否则类对象应由程序员提供。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.