使用组合来利用Java抽象集合类的继承是错误的做法吗?

使用组合来利用Java抽象集合类的继承是错误的做法吗?,java,inheritance,collections,abstract,composition,Java,Inheritance,Collections,Abstract,Composition,很多时候,当我从抽象集合类继承时,我发现自己(看起来是)将组合和继承结合在一起,我忍不住觉得这在技术上是相互冲突的设计范例 /**自定义字符串列表实现*/ 公共类MyStringList扩展了AbstractList { 私人最终摘要列表_myStringList; 公共MyStringList() { 这是。_myStringList=new ArrayList(); } 公共MyStringList(集合stringCollection) { if(stringCollection.cont

很多时候,当我从抽象集合类继承时,我发现自己(看起来是)将组合和继承结合在一起,我忍不住觉得这在技术上是相互冲突的设计范例

/**自定义字符串列表实现*/
公共类MyStringList扩展了AbstractList
{
私人最终摘要列表_myStringList;
公共MyStringList()
{
这是。_myStringList=new ArrayList();
}
公共MyStringList(集合stringCollection)
{
if(stringCollection.contains(null))
{
抛出新的NullPointerException(“MyStringList中不允许使用Null值”);
}
其他的
{
这._myStringList=新的ArrayList(stringCollection);
}
}
@凌驾
公共字符串get(int索引)
{
返回此。\u myStringList.get(索引);
}
@凌驾
公共整数大小()
{
返回此文件。_myStringList.size();
}
@凌驾
公共字符串集(整数索引、字符串搜索)
{
++这是modCount;
返回这个.u myStringList.set(index,Objects.requirennull(aString));
}
@凌驾
公共void add(int索引、字符串aString)
{
++这是modCount;
这个.u myStringList.add(index,Objects.requirennull(aString));
}
@凌驾
公共布尔加法(字符串收敛)
{
++这是modCount;
返回此项。_myStringList.add(Objects.requirennoull(aString));
}
@凌驾
删除公共字符串(整型索引)
{
++这是modCount;
返回此文件。\u myStringList.remove(索引);
}
//方法使此列表实现不同于ArrayList,以便
//这个问题就是一个例子
公共字符串[]getNumericContaining()
{
返回此文件。_myStringList.stream()
.filter(aString->aString.codePoints().anyMatch(Character::isDigit))
.toArray(字符串[]::新建);
}
//另一种使此列表实现不同于ArrayList的方法
//为了这个StackOverflow问题示例
公共字符串[]getUpperCaseContaining()
{
返回此文件。_myStringList.stream()
.filter(aString->aString.codePoints().anyMatch(字符::isUpperCase))
.toArray(字符串[]::新建);
}
}

这种将内部抽象集合对象作为该对象继承的类的支持对象(组合)的设计(继承)是否被认为是利用java.util包中定义的各种抽象集合类的“正确”方式?

您在不必要地组合东西。如果你想拥有示例中的内容,请遵循Lino的建议:

public class MyStringList extends ArrayList<String> {

    public String[] getNumericContaining() {
        return this.stream()
                   .filter(aString -> aString.codePoints().anyMatch(Character::isDigit))
                   .toArray(String[]::new);
    }

    public String[] getUpperCaseContaining() {
        return this.stream()
                   .filter(aString -> aString.codePoints().anyMatch(Character::isUpperCase))
                   .toArray(String[]::new);
    }
}
公共类MyStringList扩展了ArrayList{
公共字符串[]getNumericContaining(){
返回此.stream()
.filter(aString->aString.codePoints().anyMatch(Character::isDigit))
.toArray(字符串[]::新建);
}
公共字符串[]getUpperCaseContaining(){
返回此.stream()
.filter(aString->aString.codePoints().anyMatch(字符::isUpperCase))
.toArray(字符串[]::新建);
}
}
但是,很多时候,您只需要一个常规的
列表
,因为您可以向封装该列表的类提取额外的行为,例如

public class MySomeService {
    
    // This could be passed in, loaded from somewhere, etc.
    private List<String> foo;

    public void serviceMethod() {
        doSomething();
        String[] numbers = getNumericContaining();
        doSomethingElse(numbers);
    }

    // The previous methods
    private String[] getNumericContaining() {
        foo.stream(). // and so on
}
公共类MySomeService{
//可以从某处传入、装载等。
私人名单;
公共void服务方法(){
doSomething();
字符串[]数字=getNumericContaining();
doSomethingElse(数字);
}
//以前的方法
私有字符串[]getNumericContaining(){
foo.stream().//等等
}
这取决于你的列表本身是否真的是一个类,或者它是否只是你在某个时候需要的一种特殊味道。如果它是第一个,它可能值得拥有自己的类,如果它是后一个,它不值得


最后,尽管OOP告诉您将数据和行为放在同一个位置,但有时将它们放在不同的位置是有意义的,因此可以使用方法或函数。只需将列表作为参数传递,它将适用于所有
集合
类,而不仅仅是您的特殊类。

您应该使用这些抽象类的唯一原因是llections是指如果你要创建一种新的集合(例如“链接”列表或“数组”列表,除非它们已经存在)。你只是在创建一种新的风格,仅凭合成就可以轻松完成。“正确”始终取决于需求,如果没有任何额外的logic@Kayaman你是否有机会详细说明(或链接到解释)“种类”和“味道”之间的区别?@Lino I更新了代码片段示例,因此不允许使用
null
值,因此扩展
ArrayList
将违反ArrayList的实现。这里的示例类只是
ArrayList
的一种风格,以过于复杂的方式构建。不同类型的列表包括
ArrayList
LinkedList
CopyOnWriteArrayList
等,它们在内部设计上有很大的不同。由于您正在创建一个
ArrayList
的风格,因此扩展它会像Lino所说的那样容易得多。或者您可以使用常规列表并将额外的行为提取到其封装类中。