在Java中开发泛型修饰符的正确方法

在Java中开发泛型修饰符的正确方法,java,oop,generics,collections,Java,Oop,Generics,Collections,在Java中,我们有以下几种函数: public Collection<String> removeNulls(Collection<String> input) { List<String> output = new ArrayList<>(); // ... return output; } 对于链接列表,这变得更加微妙,如果我的函数返回一个列表,那么我将继续使用该列表。它不会有任何编译时错误,也不会有我所看到的问题。

在Java中,我们有以下几种函数:

public Collection<String> removeNulls(Collection<String> input) {
    List<String> output = new ArrayList<>();
    // ...
    return output;
}
对于
链接列表
,这变得更加微妙,如果我的函数返回一个
列表
,那么我将继续使用该列表。它不会有任何编译时错误,也不会有我所看到的问题。在前一个案例中,问题至少是可见的,因为我仍然必须将返回的集合(或列表)转换为集合,并且我知道发生了什么。问题只会在以后出现,如果有一些依赖于使用
LinkedList
的繁重处理正在进行。理想的方法(但并不意味着“推荐”)是:

public List<String> removeNulls(List<String> input) {
    List<String> output = (List<String>)input.getClass().newInstance();
    // ...
    return output;
}
public List removeNulls(列表输入){
列表输出=(列表)输入.getClass().newInstance();
// ...
返回输出;
}
通过这种方式,我可以确保至少使用相同类型的实现。你认为这个问题的解决办法是什么

编辑


只是澄清一下,我实际上没有removeNulls方法,这不是我想要的。这个问题与现实生活中的问题无关,但更多的是一个“需要思考的问题”。换句话说,我的问题是,如果您给我一个集合,我必须返回另一个以某种方式从中派生的集合,并且不修改输入,我如何确保我的输出集合符合契约(或者具体地说,实现)输入集合的类型?

我认为您的方法需要改变

如果以后需要调整不可变集合,那么创建不可变集合就没有什么意义了。集合应该在暴露于外部世界之前保持不变(如有必要)。因此,您应该只对可修改的集合进行操作,并允许您的方法调整参数

如果您从其他地方收到不可变的集合,请将其转换为可修改的集合,然后按照上述方法进行操作。

对于“排序”属性,请根据

public Collection<String> someFilter(Collection<String> input)
公共集合过滤器(集合输入)
可能以允许您保证不违反属性“排序”的方式编写,即过滤器是“稳定的”

例如,迭代输入集合并将通过过滤器的内容存储在ArrayList中,然后返回“稳定”的内容

(当然,可能会有你无法保证的转变。)


另一个特性是保留“对象标识”,而不是复制,但这有点深奥;-)

简短回答使用Scala

更严肃的回答:看看Scala集合库,尤其是

基本的想法是,如果可能的话,您提供一个能够创建适当退货收集的工厂。您基于输入参数类创建集合的想法是朝着正确方向迈出的一大步


完整的Scala版本将相当复杂,因为用户必须自己提供正确的BuildFrom,并且不能在98%的情况下依靠Scalas隐式魔术来提供正确的BuildFrom。但是研究它会给你很多想法,你可能会担心什么以及如何解决它。如果你最终真的想这么做,那就看你了。

removeNulls方法是一个非常“简单”的例子,很难想象你还会使用什么类型的“修饰符”

根据你的描述,听起来你的实际意图可能会得到新计划的很好支持。例如,从
流中删除
null
元素可以通过

// Filter out all null elements
collection.stream().filter((e) -> e != null).... // Further operations
这里的好处是,您的实际问题是在内部处理的:在操作期间(尽可能)保留流的名称。例如,以前排序过的流将保持排序状态


对于Java-8之前的版本,处理这种情况的另一种方法是指定输出集合(或者至少提供这样做的选项)。例如:您可以将该方法编写为

public <T> Collection<T> removeNulls(Collection<? extends T> input, Collection<T> output)
{
    if (output == null) 
    {
        // Create some default collection or
        // report an error...
    }
    for (T t : input)
    {
        if (t != null)
        {
            output.add(t);
        }
    }
    return output;
}

// Convenience method
public <T> Collection<T> removeNulls(Collection<? extends T> input)
{
    return removeNulls(input, new ArrayList<T>());
}

我想,您想要的是使用操作
修改集合接口,而不使用null
,并相应地实现它,但是由于您无法将新方法“添加”到现有集合类中,因此您运气不佳。但是,由于您经常使用自己的集合实现,也许可以扩展接口并提供一些方法将常规集合类转换为自己的集合类

大概是这样的:

public interface BetterCollection<E> extends Collection<E> {
    BetterCollection<E> withoutNulls();
}

public class BetterLinkedList<E> extends LinkedList<E> implements BetterCollection<E> {
    public BetterLinkedList() {}

    public BetterLinkedList(LinkedList<E> input) {
        super(input);
    }

    @Override
    public BetterCollection<E> withoutNulls() {
        BetterCollection<E> result = new BetterLinkedList<E>();
        ...
        return result;
    }
}
公共接口BetterCollection扩展了集合{
BetterCollection without nulls();
}
公共类BetterLinkedList扩展LinkedList实现BetterCollection{
公共BetterLinkedList(){}
公共BetterLinkedList(LinkedList输入){
超级(输入);
}
@凌驾
不带Nulls的公共BetterCollection(){
BetterCollection结果=新建BetterLinkedList();
...
返回结果;
}
}
我认为在C#中可以使用扩展方法,但是您在实现上有一些限制(例如,您不能访问私有字段,我不确定它们是否可以是虚拟的)


Java中的另一个解决方案是重载方法并使它们接受不同的集合类型,如
removeNulls(LinkedList)
removeNulls(SortedSet)
,但它显然也有其缺点。

如果不能开发更统一的API,最好提出关键的假设(尤其是那些无法以编程方式强制执行的)作为文档中的一个先决条件,因此当程序员不正确地使用库函数时,打破该先决条件的程序员除了自己之外没有其他人可以责怪。在我看来,确实存在一些API设计问题……我认为重点是输入/原始集合可能不正确
Set<String> mySet = new SortedSet<>();
mySet.add(10);
mySet.add(9); // I know that my collection is now sorted
Collection<String> myFilteredSet = removeNulls(mySet, new SortedSet<>());
/** 
 * Creates a new collection with the same contents as the given collection,
 * but without any <code>null</code> entries
 *
 * The order of output elements will be the same as the input elements
 * or alternatively:
 * No assumptions may be made about the order of the output collection
 *
 * @return The filtered collection
 */
public interface BetterCollection<E> extends Collection<E> {
    BetterCollection<E> withoutNulls();
}

public class BetterLinkedList<E> extends LinkedList<E> implements BetterCollection<E> {
    public BetterLinkedList() {}

    public BetterLinkedList(LinkedList<E> input) {
        super(input);
    }

    @Override
    public BetterCollection<E> withoutNulls() {
        BetterCollection<E> result = new BetterLinkedList<E>();
        ...
        return result;
    }
}