简体   繁体   English

如何返回作为参数给出的相同Collection类型,但具有不同的元素类型?

[英]How can I return the same Collection type given as a parameter, but with a different element type?

I want to do this: 我想做这个:

public <C extends Collection<?>> C<File> strings_to_files(C<String> strings)
{
    C<File> files = new C<File>();

    for(String string : strings)
    {
        files.add(new File(string)
    }

    return files;
}

I just want it to take any Collection of strings, and return that same Collection type with files. 我只是想让它采取任何字符串集合,并返回与文件相同的集合类型。 Is there any way to do this? 有没有办法做到这一点? Or maybe I just have the syntax wrong... 或者我可能只是语法错误...

There's no good way to do that directly. 直接做到这一点没有好办法。 The easiest thing, I think, would be to pass the target collection as a second argument: 我认为最简单的方法是将目标集合作为第二个参数传递:

public void string_to_files(Collection<String> strings, Collection<File> files) {
    for(String string : strings) {
        files.add(new File(string));
    }
}

The client code can then decide what type of collection it wants back. 然后,客户端代码可以决定它想要回收的集合类型。 This doesn't do what you asked, but it's the cleanest way to avoid having to cast and suppress warnings. 这不符合您的要求,但它是避免必须施放和抑制警告的最简洁方法。

Alternatively, just declare the method to return a Collection<File> (or a specific Collection implementation type) and instantiate a specific Collection type of your choice. 或者,只需声明方法以返回Collection<File> (或特定的Collection实现类型)并实例化您选择的特定Collection类型。

Let's pretend that your code compiles, and you pass in List . 让我们假装您的代码编译,然后传入List You'll have a compile error, since List is an interface, not a concrete class. 您将遇到编译错误,因为List是一个接口,而不是具体的类。

public static List<File> strings_to_files(List<String> strings)
{
    List<File> files = new List<File>(); // compile error, since this is an interface

    for(String string : strings)
    {
        files.add(new File(string));
    }

    return files;
}

You could do some magic with reflection: 你可以用反射做一些魔术:

public Collection<File> strings_to_files(Collection<String> strings)
{
    Collection<File> files = null;
    try {
        Constructor ctor = findDefaultConstructor(strings.getClass());
        files = (Collection<File>) ctor.newInstance();
    } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
            | SecurityException e) {
        throw new RuntimeException(e);
    }

    for(String string : strings)
    {
        files.add(new File(string));
    }

    return files;
}

private Constructor findDefaultConstructor(Class collectionClass) {
    for (Constructor ctor : collectionClass.getDeclaredConstructors()) {
        System.out.println(ctor);
        if (ctor.getParameterCount() == 0) {
            ctor.setAccessible(true);
            return ctor;
        }
    }
    return null;
}

This magic will work for classes, like ArrayList or HashSet . 这种魔法适用于类,如ArrayListHashSet However, it won't work for lists created by Arrays.asList(...) . 但是,它不适用于Arrays.asList(...)创建的列表。 So, there's no good fix for this within this method. 所以, 这种方法中没有很好的解决方案。 However, you can pass the responsibility to the caller of this method. 但是,您可以将责任传递给此方法的调用者。

Since all the standard collections except Map implement the Collection interface, which means, they all comply with the Collection interface and can be treated a Collection , so you don't really need to write <C extends Collection<?>> . 由于除Map之外的所有标准集合都实现了Collection接口,这意味着它们都符合Collection接口并且可以被视为Collection ,因此您不需要编写<C extends Collection<?>> Your function must use one of the concrete classes which implements Collection to store the files because you will use new to initialize it, meaning it must be concrete. 您的函数必须使用一个实现Collection的具体类来存储文件,因为您将使用new来初始化它,这意味着它必须具体。 However, if you want to hide this detail, just use Collection<File> as the return type. 但是,如果要隐藏此详细信息,只需使用Collection<File>作为返回类型。

public Collection<File> strings_to_files(Collection<String> strings)
{
    Collection<File> files = new ArrayList<>();

    for(String string : strings)
    {
        files.add(new File(string));
    }

    return files;
}

In Java 8, the function body can be simplified as : 在Java 8中,函数体可以简化为:

return strings.stream().map(File::new).collect(Collectors.toCollection(ArrayList::new));

Or: 要么:

return strings.stream().map(File::new).collect(Collectors.toList());

You can do the same as @ted-hopp suggested and enforce the invariant with a runtime check: 您可以像@ ted-hopp建议的那样执行相同操作并使用运行时检查强制执行不变量:

public void strings_to_files(Collection<String> strings, Collection<File> files) {
    if (!strings.getClass().equals(files.getClass())) {
        throw new IllegalArgumentException("strings and files must belong to the same collection type");
    }
    for(String string : strings) {
        files.add(new File(string));
    }
}

I think these are two seperate questions: 我认为这是两个单独的问题:
1. How to create a new collection of the same type you get as an argument. 1.如何创建一个与参数相同类型的新集合。
2. How to express in the function declaration that the caller is garantueed to get the same type of collection as he passed in. 2.如何在函数声明中表示调用者被授权获得与传入的相同类型的集合。


The easier question is 2.: 更容易的问题是2:
Not possible. 不可能。 Since generic types cannot have a generic type parameter themselves, this does not work. 由于泛型类型本身不能具有泛型类型参数,因此不起作用。 You can put it in the documentation, but the function declaration would have to look like this: 您可以将它放在文档中,但函数声明必须如下所示:

/**
 * @return The same type of collection as was passed as argument.
 */
public Collection<File> strings_to_files(Collection<String> strings) {}

The caller will have to cast the result back to the type that was passed: 调用者必须将结果强制转换为传递的类型:

ArrayList<File> files = (ArrayList<File>)strings_to_files(new ArrayList<String>());

Question 1.: 问题1:
One of Tamas Rev 's answers provided a possibility. Tamas Rev的一个答案提供了一种可能性。

However, if you do not want to use reflection, you can create you own collections which provide the methods to recreate collections of the same type or a copy on every collection object itself. 但是,如果您不想使用反射,则可以创建自己的集合,这些集合提供了在每个集合对象本身上重新创建相同类型集合或副本的方法。 This is actually pretty simple: 这实际上非常简单:

public interface Collection<E> extends java.util.Collection<E> {

    /**
     * @return Shallow copy with the same collection type as this objects real collection type.
     */
    public Collection<E> copy();

    /**
     * @return New empty instance of the same type as this objects real collections type.
     */
    public <F> Collection<F> newInstance();
}

public interface List<E> extends java.util.List<E>, Collection<E> {
    @Override
    public List<E> copy();
    @Override
    public <F> List<F> newInstance();
}

public class LinkedList<E> extends java.util.LinkedList<E> implements List<E> {
    @Override
    public LinkedList<E> copy() {
        return new LinkedList<E>(this);
    }
    @Override
    public <F> LinkedList<F> newInstance() {
        return new LinkedList<F>();
    }
}

You need to do this with every Collection class you need. 您需要对所需的每个Collection类执行此操作。 Now you can use those collections in your function: 现在您可以在函数中使用这些集合:

public Collection<File> strings_to_files(Collection<String> strings)
{
    Collection<File> files = strings.<File>newInstance();

    for(String string : strings)
    {
        files.add(new File(string));
    }

    return files;
}

You can still pass your collections to other libraries. 您仍然可以将集合传递给其他库。 Another advantage is that you can put other useful things into your collections like an actual class object of the generic parameter, so you can always enforce and retrieve the generic parameter type. 另一个优点是,您可以将其他有用的东西放入集合中,就像泛型参数的实际类对象一样,因此您始终可以强制执行并检索泛型参数类型。
The only problem is, default collections need to be converted to your collections if you want to use them. 唯一的问题是,如果要使用默认集合,则需要将其转换为集合。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在具有相同类型参数的Java中创建方法? - How Can I Create method In Java With Same Type Parameter? 如何减少多个采用不同类型1个参数的方法? - How can I reduce multiple methods that take 1 parameter of different type? 泛型-函数返回类型是否与参数类型相同? - Generics - Function return type the same as the parameter type? 如何从单个函数返回不同类型的对象? - How can I return objects of different type from a single function? 如何返回我显式传递为参数的类型? - How to return type I explicitly passed as parameter? 如何将具有相同返回类型的不同通用类型的列表数据放入? - How to put List data with different generic type with same Return type? 如何在不同的类型ArrayList中使用相同的方法? - How can I use same methods in different classes type ArrayList? 如何使用不同的参数类型保持相同的 function - How can i keep the same function with different argument type 返回作为参数提供给方法的类型列表? - Return a List of type that is given as parameter to a method? 如何消除/删除在数组中重复大量时间的 object 数组元素的重复。 (返回相同的数组类型) - How can I eliminate/remove a duplication of an object array element which is duplicated a ton of time in the array. (Return same array type)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM