• Java泛型


    泛型是Java 5引入的机制, 允许编写不关心具体类型的类或方法. 泛型最著名的应用是Collection框架.

    List<String> list = new ArrayList<>();
    

    泛型类List在定义时并不关心元素类型, 只有在实例化时才获得具体的元素类型.

    泛型类

    声明泛型需要使用<>声明类型参数, 如<T>, <T1, T2>. 下面的示例中声明了一个Holder类, 它的item域可以存储任意类型的引用.

    class Holder<T> {
    
        T item;
    
        public void set(T t) {
            item = t;
        }
    
        public T get() {
            return item;
        }
    
        public static void main(String[] args) {
            Holder<String> holder = new Holder<>();
            holder.set("Hello World");
            System.out.println(holder.get());
        }
    }
    

    在Java 7之前必须在创建实例时指定类型参数:

    Holder<String> holder = new Holder<String>();
    

    Java 7提供了类型推断功能, 可以根据声明推断实例的类型参数:

    Holder<String> holder = new Holder<>();  // 不必在new对象时再写一次类型参数了
    

    或者在return时推断类型:

    public static Holder<String> getInstance() {
        return new Holder<>();
    }
    

    示例中的Holder类没有对类型参数T做任何限制, T可以实例化为任意类型. Java允许我们限制对其进行限制.

    • <T extends MyClass>: T必须为MyClass或其子类

    • <T super MyClass>: T必须为MyClass或其父类

    比较常用的是extends限定, 因为子类必然定义了父类的方法(和域), 因此我们可以安全地访问父类声明的方法(和域).

    下面的示例中访问item.length()是安全的, 因为T的基类String定义了该方法.

    class Holder<T extends String> {
    
        T item;
    
        public void set(T t) {
            item = t;
        }
    
        public T get() {
            return item;
        }
    
        public int length() {
            return item.length();
        }
    
        public static void main(String[] args) {
            Holder<String> holder = new Holder<>();
            holder.set("Hello World");
            System.out.println(holder.length());
        }
    
    }
    

    泛型方法

    Java也允许只为方法而非整个类声明类型参数:

    public class Main {
    
        public static <T> void log(T t) {
            System.out.println(t);
        }
        
        public static void main(String[] args) {
            log(1);
        }
    }
    

    类型通配符

    在上文示例中, 我们总是在实例化泛型类时指定具体的类代替类型参数, 比如Holder<String>使用String代替类型参数T.

    类型通配符允许在实例化泛型类时不指定具体类:

    Holder<?> holder = new Holder<>();
    

    上述示例初始化了一个没有类型限制的Holder实例. 无任何限制的类型通配符可以被省略:

    Holder holder = new Holder();
    

    除非因为绝对必要的原因, 否则不建议使用无限制的类型通配符. 类型通配符同样可以使用extendssuper进行范围限定.

    Holder<? extends String> holder = new Holder<>();
    

    上述示例中, holderset方法不能正常编译. 类型通配符通常用于声明方法的参数类型:

    
    class Holder<T> {
    
        T item;
    
        public void set(T t) {
            item = t;
        }
    
        public T get() {
            return item;
        }
    
        public static void print(Holder<? extends String> holder) {
            System.out.println(holder.get());
        }
    
        public static void main(String[] args) {
            Holder<String> holder = new Holder<>();
            holder.set("Hello World");
            print(holder);
        }
    
    }
    

    类型擦除

    java的泛型采用运行时类型擦除的方式实现泛型, 也就是说类型参数仅存在于编译期, 运行时虚拟机并不知道泛型参数的存在.

    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        List<Long> longs = new ArrayList<>();
        System.out.println(strings.getClass() == longs.getClass());
    }
    

    上文示例输出true, 说明了运行期无法访问类型参数. 泛型是通过编译时添加了类型检查和自动转型的字节码来实现的.

    数组也受到了类型擦除影响:

    public class Main {
    
        public static <T> void print(T[] arr) {
            for (T t : arr) {
                System.out.println(t);
            }
        }
    
        public static void main(String[] args) {
            String[] strings = {"a", "b", "c", "d"};
            print(strings);
        }
    }
    

    上面的代码是可以正常运行的, 但是Java禁止直接创建泛型数组:

        public static <T> void test() {
            T[] arr = new T[5];
        }
    

    禁止创建泛型数组的原因可以在Java Language Specification中窥见端倪:

    10.6 数组初始化中提到

    [It is a compile-time error if the component type of the array being initialized is not reifiable(4.7)

    看一下reifiable(物化)的定义:

    A type is reifiable if and only if one of the following holds:
    It refers to a non-generic class or interface type declaration.
    It is a parameterized type in which all type arguments are unbounded wildcards (§4.5.1).
    It is a raw type (§4.8).
    It is a primitive type (§4.2).
    It is an array type (§10.1) whose element type is reifiable.

    因为类型擦除的原因, java.util.ArrayList采用了Object[]来存储元素.

  • 相关阅读:
    codeforces 261B Maxim and Restaurant(概率DP)
    洛谷P3066 [USACO12DEC]逃跑的Barn (线段树合并)
    洛谷P1600 天天爱跑步(线段树合并)
    AtCoder
    SPOJ10606 BALNUM
    洛谷P3567[POI2014]KUR-Couriers(主席树+二分)
    洛谷P2633 Count on a tree(主席树上树)
    【.Net边角料系列】1-单例模式(我真不是你想的那样)
    生成二维码的开源工具对比(附源码了呀!)
    你所不知道的linq(二)
  • 原文地址:https://www.cnblogs.com/Finley/p/7787575.html
Copyright © 2020-2023  润新知