我知道基于对象的集合是泛型的主要候选者,并且已经在集合库中泛型。除此之外,一个类或接口应该具备哪些属性和行为才能使其成为泛化的候选者?为什么例如 ThreadLocal
类或AtomicReference
类被泛化?是什么导致了这个设计决策?
5 回答
需要为不同类型重用的类始终是泛型的候选者。例如class Pair<U,V>
如果一个类可以与各种其他类具有相同的关系,那么该类是泛化的一个很好的候选者。集合类是通用的,因为关系“包含”可以应用于任何对象类。Comparable
和接口是通用的Comparator
,因为“可以比较”的关系可以应用于许多不同的对象(尽管实际上这仅限于类自己的层次结构中和周围的对象Comparable
)。
此外,如果您发现自己编写了多个非常相似的类,唯一的区别是您正在操作的对象的类型,那么请考虑编写一个泛型类。
编辑:
使用内置类AtomicReference
和ThreadLocal
,“包含”关系适用于各种对象类型。一个AtomicReference
和一个ThreadLocal
对象仍然包含另一个类型的对象。这允许以类型安全的方式设置和返回特定类型,而无需让“get”和“set”方法返回/获取,Object
并且在调用“get”时无需转换返回类型“ 方法。该关系适用于特定类型,但在类设计时不需要知道确切的类型。
编辑2:
我能想到的用于使类泛型的大多数示例都涉及“包含”关系。然而,这只是泛型关系的一个具体示例,尽管 Collections 框架是基于“包含”关系的,并且它提供了许多“包含”泛型类。泛型关系不一定是“包含”,所以我将尝试举例说明可以泛化但关系是“包含”的地方。
想象一个抽象Shape
类,具有具体的类,如Line
、Square
、Circle
等。现在我们定义ShapeDrawer
知道如何绘制形状的接口。这里的关系是“平局”。我们可以定义接口:
public interface ShapeDrawer {
public void drawShape(Shape shape);
}
现在我们需要ShapeDrawer
专门针对特定形状的特定具体实现,例如CircleDrawer
和SquareDrawer
。
public class CircleDrawer implements ShapeDrawer {
public void drawShape(Shape shape) {/*...*/}
}
现在drawShape
方法 inCircleDrawer
必须在绘制它之前测试它shape
是否是 a 。Circle
现在,使用泛型的解决方案:
public interface ShapeDrawer<T extends Shape> {
public void drawShape(T shape);
}
现在,一个实现可以绘制一个特定的Shape
:
public class CircleDrawer implements ShapeDrawer<Circle> {
public void drawShape(Circle circle) {/*...*/}
}
数据结构应该是通用的,因为该结构应该能够处理不止一种类型的输入。
任何可以/应该被一种以上数据类型使用的类都应该以通用方式创建。
不要寻找“泛化”的东西。相反,您应该使用泛型以您认为最合适的方式完成特定任务。泛型只是工具箱中用于完成任务的工具。
如果您的问题是什么类型的东西可能是最好的通用实现,那么您自己的示例就是一个主要示例。当您需要以类似的方式处理不同的结构(如列表)时,泛型是一个很好的解决方案。这包括以类不变的方式处理其他类的大多数类,或者在类的子部分上是不变的。
每当您发现自己在铸造一个值时,您都应该考虑泛化是否会让您删除该铸造。让我们举个例子,ThreadLocal
和AtomicReference
。两次使用返回的值get()
都需要强制转换(至少在大多数情况下)。
请记住,泛型可能非常嘈杂。权衡成本和收益。尤其要考虑泛型化是否有利于 API 用户或只是你,实施者。