在 Java 中对静态帮助程序类使用泛型还是对象类型更好



前几天我正在编写一个程序,要求我:获取ArrayList<String>内特定对象的频率,删除给定项目的所有出现次数,等等,List接口未指定的。我决定编写自己的帮助程序类,并希望使其尽可能可重用。我决定将List指定为集合的参数类型,以便我可以将其用于实现List接口的任何类。但是这些类通常使用泛型定义,我不知道要删除的项目会是什么类类型。所以我要么必须通用地定义静态帮助程序方法,因为静态类不能显式包含泛型类型,要么将要删除的对象的类类型定义为Object。我以两种方式实现了它,见下文,但我想知道使用一种方式是否有任何好处。

关于该主题的一些进一步问题:

  1. 为什么我能够通过在方法标头而不是类标头中定义泛型类型的引用来解决静态上下文中的泛型类型的引用?
  2. 当使用这个静态方法时,为什么我不必在其用法中声明类Type?即 ListTools_V2.getFrequencyOf(ArrayList<String> items, String s)仍然有效。

使用 Object 类类型的实现

import java.util.List;
/** General utility class for performing frequently needed operations
    on any class implementing the List interface **/ 
public class ListTools {
    public static void removeAllOccurrences(List items, Object o) {
        while(items.contains(o)) {
            items.remove(o);
        }
    }
    public static int getFrequencyOf(List items, Object o) {
        int frequency = 0;
        for(Object item : items) {
            if(item.equals(o)) {
                frequency++;
            }
        }
        return frequency;
    }
}

使用泛型实现

import java.util.List;
/** General utility class for performing frequently needed operations
    on any class implementing the List interface. This implementation
    uses generics to maximize reusability. **/ 
public class ListTools_V2 {
    public static <E> void removeAllOccurrences(List<E> items, E o) {
        while(items.contains(o)) {
            items.remove(o);
        }
    }
    public static <E> int getFrequencyOf(List<E> items,E o) {
        int frequency = 0;
        for(E item : items) {
            if(item.equals(o)) {
                frequency++;
            }
        }
        return frequency;
    }
}

这两个操作都对给定对象引用和列表中元素之间的相等(.equals())进行操作,并且相等不限于相同类型的对象,因此不应o限制为与列表的类型参数相同的类型。

但是,原始类型

是不好的,因此不应使用原始类型List。当不需要对任何内容约束类型变量时,您应该使用通配符对其进行参数化:

public static void removeAllOccurrences(List<?> items, Object o)
public static int getFrequencyOf(List<?> items, Object o)

型更好,因为它在编译时检测与类型相关的问题。
在运行时发生"擦除",泛型没有"意义"。

例如:

List apples = new ArrayList();
apples.add(new Apple());
apples.add(new Mango()); //compiles, but this is wrong
apples.add(new Chair()); //compiles, but this is wrong



当然,您必须了解通用机制的各种"陷阱"。我很容易想到的例子是:
List<Mango> 不扩展List<Fruit>,因此您应该使用List<? super Fruit>List<? extends Fruit>

您不能执行以下操作:

T.getClassName()

型是实现此类类的推荐方法。它们可用于向用户(使用此类的用户)提供一些隐式信息。例如:

List<Cat> cats = ...;
ListTools.removeAllOccurrences(cats, new Dog());

这是没有意义的,因为猫名单中不可能有任何狗。但是用户可以执行此代码并想象他已从猫列表中删除了一些狗。另一方面,通用版本会通知用户此不适当的调用。

List<Cat> cats = ...;
ListTools_V2.removeAllOccurrences(cats, new Dog()); // Compile time error because it is pointless
此外,还有

性能提升。如果猫列表包含数千只猫,你真的想在大量的猫列表中搜索那只狗吗?(包含实际搜索整个列表的方法)还是您只是想在编译时避免它?;)

您的getFrequencyOf()方法主体在这两种情况下基本相同,除了在一种情况下您传递原始List,而在另一种情况下您正在参数化它以接受任何类型。

1-

public static int getFrequencyOf(List items, Object o) {
        int frequency = 0;
        for(Object item : items) {
            if(item.equals(o)) {
                frequency++;
            }
        }
        return frequency;
    }

此处列表是原始类型。对泛型类型 List 的引用应该参数化,但事实并非如此。但它对任何类型的列表(整数、长整型、字符串等)都是开放的。

阿拉伯数字-

public static <E> int getFrequencyOf(List<E> items,E o) {
        int frequency = 0;
        for(E item : items) {
            if(item.equals(o)) {
                frequency++;
            }
        }
        return frequency;
    }

在这里,您的列表不是原始类型。对泛型类型 List 的引用应该参数化,它是。这也对任何类型的列表(整数,长整型,字符串等)开放。

最新更新