Java 泛型

1 泛型方法

无论在何时,只要你能做到,你就应该尽量使用泛型方法。

定义泛型方法:将泛型参数置于返回值之前:

public void f(T x){}

1.1 类型参数推断

当使用泛系类时,必须在创建对象的时候指定类型参数值,而使用泛系方法的时候,通常不必指明参数类型,因为编译器会为我们找出具体的类型。这称为类型参数推断。

参数推断只对赋值操作有效,其他时候并不起作用[jdk6环境,该结论不成立]。

    public  T f(T t) {
        return t;
    }

    public  void  f2(T t) {
        System.out.println(t);
    }

    public static void main(String[] args) {
        Methods methods = new Methods();
        methods.f2(methods.f("Hello"));
        methods.f2(methods.f(3));
        methods.f2(methods.f(new ArrayList()));
//        运行结果:
//        Hello
//        3
//        []
    }

2 擦除

类型参数将擦除到它的第一个边界。“边界就是发生动作的地方。”

2.1 边界

extends 关键字在泛型边界上下文环境中和普通情况下所具有的意义是完全不同的。

public class Boundary {

    static class Paper {
        String content;
    }

    interface Pen {
        void write();
    }

    interface Horn {
        void speak();
    }

    static class Student {

        T entity;

        public Student(T entity) {
            this.entity = entity;
        }

        public T getEntity() {
            return entity;
        }

        void write() {
            entity.write();
        }

        void speak() {
            entity.speak();
        }

    }

    static class Media extends Paper implements Pen, Horn {

        @Override
        public void write() {
            content = "为中华崛起而读书";
        }

        @Override
        public void speak() {
            System.out.println(content);
        }

    }

    public static void main(String[] args) {

        Student student = new Student<>(new Media());
        student.write();
        student.speak();;
//        运行结果:
//        为中华崛起而读书
    }

}

2.2 通配符

    static class Fruit {}
    static class Apple extends Fruit {}
    static class Jonathan extends Apple {}
    static class Orange extends Fruit {}

    @Test
    public void test02() {
        List< ? extends Fruit > fruits = new ArrayList<>();
//        fruits.add(new Apple());  Required type: capture of ? extends Fruit
//        fruits.add(new Fruit()) Required type: capture of ? extends Fruit
        fruits.add(null);
        Fruit fruit = fruits.get(0);

        fruits = Arrays.asList(new Apple(),new Fruit(),new Orange());
        Apple apple = (Apple) fruits.get(0);

    }

List 可以合法地指向一个List。一旦执行这种类型的向上转型,你就将丢失掉向其中传递任何对象的能力,甚至是传递Object也不行。

另一方面,如果你调用一个返回Fruit的方法,则是安全的,因为你知道在这个List中的任何对象至少具有Fruit类型,因此编译器将允许这么做。

2.2.1 逆变

超类型通配符(下界通配符),可以声明通配符是由某个特定类的任何基类来界定的。或。不能对泛型参数给出一个超类型边界,即不能声明

    @Test
    public void test03() {
        List< ? super Apple > fruits = new ArrayList();
        fruits.add(new Apple());
        fruits.add(new Jonathan());
//        fruits.add(new Fruit()); error

        Object object = fruits.get(0);
        Jonathan jonathan = (Jonathan) fruits.get(1);
        System.out.println("object:" + object.getClass() + ",jonathan:" + jonathan.getClass());
//        运行结果:
//        object:class com.juxinma.article.Wildcard$Apple,jonathan:class com.juxinma.article.Wildcard$Jonathan
    }

List list = new ArrayList() //正确,下界是Apple

list.add(new Apple()); //正确,子类型的引用可以赋值给父类对象的引用

2.2.2 无界限通配符

无界限通配符 等价于

Map map;

Map map;

Map map;

    static class Holder {
        T t;
        public Holder(T t) {
            this.t = t;
        }

        public T getT() {
            return t;
        }
    }

    static  void  f1(Holder holder) {
        T t = holder.getT();
        System.out.println(t.getClass().getSimpleName());
    }

    static void f2(Holder holder) {
        f1(holder);
    }

    public static void main(String[] args) {
        Holder holder = new Holder(1);
        f1(holder); //warnings, Unchecked assignment:
        f2(holder); // no warnings
    }

如果向一个使用的方法传递原生类型,对于编译器来说,可能会推断出实际的类型参数,使得这个方法可以回转并调用另一个使用确切类型的方法,它被称为捕获转换

3 自限定的类型

3.1 古怪的循环泛型

不能直接继承一个泛型参数,但是,可以继承在其自己的定义中使用这个泛型参数的类:

class Fruit {}

class Apple extends Fruit {}

作用:能产生使用导出类作为其参数和返回类型的基类。还能将导出类型用作其域类型,甚至那些将被擦擦为Object的类型。

public class SelfLimit {

    static class BasicHolder {
        T element;

        public T getElement() {
            return element;
        }

        public void setElement(T element) {
            this.element = element;
        }

        void f() {
            System.out.println(element.getClass().getSimpleName());
        }

    }

    static class Subtype extends BasicHolder {}

    public static void main(String[] args) {
        Subtype st1 = new Subtype();
        Subtype st2 = new Subtype();
        st1.setElement(st2);
        Subtype st3 = st1.getElement();
        st1.f();;
//        运行结果:
//        Subtype
    }

}

3.2 参数协变

方法的参数类型会随子类而改变,协变返回类型是Java SE5引入的。

public class SelfLimit2 {

    static class Base {}
    static class Derived extends Base {}

    interface BaseGetter {
        Base get();
    }

    interface DerivedGetter extends BaseGetter{
        Derived get();
    }

    static class DerivedGetterImpl implements DerivedGetter {
        @Override
        public Derived get() {
            System.out.println("derivedGetter.get");
            return null;
        }
    }

    void testDerivedGetter(DerivedGetter derivedGetter) {
        Derived derived = derivedGetter.get();
        Base base = derivedGetter.get();
    }

    @Test
    public void test1() {
        DerivedGetterImpl derivedGetter = new DerivedGetterImpl();
        testDerivedGetter(derivedGetter);
//        运行结果:
//        derivedGetter.get
//        derivedGetter.get
    }

    interface CovarianceSetter> {
        void set(T t);
    }

    interface Setter extends CovarianceSetter {}

    static class GetterImpl implements Setter {
        @Override
        public void set(Setter setter) {
            System.out.println(setter.getClass().getSimpleName());
        }
    }

    void setterTest(Setter setter, Setter setter2, CovarianceSetter covarianceSetter) {
        setter.set(setter2);
//        setter.set(covarianceSetter); error
    }

}

4 动态类型安全

因为可以向Java SE5之前的代码传递泛型容器,所以旧式代码仍旧有可能会破话你的容器,Collections的静态方法:checkedCollection、checkedList、checkedMap、checkedSet、checkedSortedMap和checkedSortedSet。会将你希望动态检查的容器当做第一个参数接受,并将你希望强制要求的类型作为第二个参数接受。

受检查的容器在你试图插入类型不正确的对象时抛出ClassCastException。

public class DynamicSafe {

    public void addToList(List list,Object object) {
        list.add(object);
    }

    @Test
    public void test() {
        List doubleList = new ArrayList<>();
        addToList(doubleList,2.0d); //正常
        addToList(doubleList,"hello"); //正常

        List checkedDoubleList = Collections.checkedList(doubleList, Double.class);
        addToList(checkedDoubleList, 3.0d); //正常
        addToList(checkedDoubleList,"java");//报异常,java.lang.ClassCastException
    }

}

5 异常

catch语句不能捕获泛型类型的异常,因为在编译期和运行时都必须知道异常的确切类型。泛型类也不能直接或间接继承自Throwable。

但是,类型参数可能会在一个方法的throws子句中用到。

interface Processor {

      void process(List result) throws E;

}

你可能感兴趣的:(Java编程思想,java,开发语言)