Java泛型

泛型的优点

  1. 泛型应该说是一种高级的抽象,类型的参数化,加强程序的通用性;

  2. 类型安全,防止ClasscastException;

  3. 泛型化后,编译器会帮助我们进行类型推导,类型自动转化,消除强制类型转换,由编译器更安全地完成;

  4. 大大增强容器类的通用性(说不定引入泛型初衷,就是为了更好的使用容器类)。


泛型自底向上

  1. 一般情况下,方法的参数只能为固定的类型(基本类型、特定的类型),没有通用性可言;

  2. 把方法的参数设为基类,则该方法可以接受任何基类的子类,所有基类的类型通用;

  3. 更高的抽象(接口),方法参数设为接口,接受范围更广泛,所有接口类型通用;

  4. 终极,参数化类型,方法参数的类型当做参数,即就是泛型,任何类型通用。


简单泛型

设计一个持有单个对象的容器:

public class Holder1 {
    private Object a;
    public Holder1(Object a) { 
        this.a = a; 
    }
    Object get() { 
        return a; 
    }
}

这个容器确实能持有多种类型的对象,但通常只会用它来存储一种对象。虽然设计时希望能存储任意类型,但使用时却能够只存储我们想要的确定类型。

泛型也可以实现这样的功能,而且提供编译期检查:

class Automobile {}

public class Holder2<T> {
    private T a;
    public Holder2(T a) { 
          this.a = a; 
    }
    public void set(T a) {
          this.a = a;
    }
    public T get() {
         return a; 
    }

    public static void main(String[] args) {
        Holder2<Automobile> h2 =
            new Holder2<Automobile>(new Automobile());
            Automobile a = h2.get(); // No cast needed
            // h2.set("Not an Automobile"); // Error
            // h2.set(1); // Error
    }
}

泛型方法

泛型可以应用于方法,只需要将泛型参数列表放在方法返回值之前即可。

public class GenericMethods {
    public <T> void f(T x) {
        System.out.println(x.getClass().getName());
    }
    public static void main(String[] args) {
        GenericMethods gm = new GenericMethods();
        gm.f("");
        gm.f(1);
        gm.f(1.0);
        gm.f(1.0F);
        gm.f(‘c’);
        gm.f(gm);
    }
} /* Output:
java.lang.String
java.lang.Integer
java.lang.Double
java.lang.Float
java.lang.Character
GenericMethods

如果调用f()时传入了基本数据类型,自动打包机制将会被触发


类型擦除

Java是伪泛型,实际上是进行的泛型擦除,在编译就已经完成泛型的相关转型代码的插入。

ArrayList用来存储数据的数组是这样定义的:

/**
 * The elements in this list, followed by nulls.
 */
transient Object[] array;

get方法:

@SuppressWarnings("unchecked") @Override public E get(int index) {
        if (index >= size) {
            throwIndexOutOfBoundsException(index, size);
        }
        return (E) array[index];
    }

通配符

指定上界List<? extends Fruit>

import java.util.*;
public class GenericsAndCovariance {
    public static void main(String[] args) {
        // Wildcards allow covariance:
        List<? extends Fruit> flist = new ArrayList<Apple>();
        // Compile Error: can’t add any type of object:
        // flist.add(new Apple());
        // flist.add(new Fruit());
        // flist.add(new Object());
        flist.add(null); // Legal but uninteresting
        // We know that it returns at least Fruit:
        Fruit f = flist.get(0);
    }
} ///:~

flist的类型为List<? extends Fruit>,读作“任何从Fruit继承而来的类型构成的列表”。但这并不意味着这个List将持有任何类型的Fruit,通配符引用的其实是明确的类型,这个例子中它意味着“某种指定了上界为Fruit的具体类型”。

add方法不可用是因为他的参数变为? extends Fruit,其接受的参数可以是任意类型,只需满足上界为Fruit即可,而编译器无法验证“任意类型”的类型安全性。

指定下界List<? super Apple> apples

class Jonathan extends Apple {}

public class SuperTypeWildcards {
    static void writeTo(List<? super Apple> apples) {
        apples.add(new Apple());
        apples.add(new Jonathan());
        // apples.add(new Fruit()); // Error
    }
} ///:~

与上界相反,可以使用add方法。


参数化接口

一个类不能实现同一个泛型接口的两种变体,因为擦除会让它们变成相同的接口:

interface Payable<T> {}

class Employee implements Payable<Employee> {}

class Hourly extends Employee
    implements Payable<Hourly> {}

Hourly不能编译。但是,如果从Payable的两种用法中移除掉泛型参数(就像编译器在擦除阶段做的那样),这段代码将能够编译。


重载

类型擦除会让两个方法产生相同的签名:

public class UseList<W,T> {
    void f(List<T> v) {}
    void f(List<W> v) {}
}

想要其使用就要有不同的返回值(略不优雅)

参考:Generics泛型

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值