2019-07-16 Java语法糖-泛型-类关系

       在考虑泛型类之间关系之前,我们需要熟悉以下术语,用于描述程序设计语言的类型关系:

协变(Covariance):使你能够使用比原始指定的类型派生程度更大的类型。你可以向List类型的变量分配List

逆变(Contravariance):使你能够使用比原始指定的类型更泛型(派生程度更小)的类型。你可以向List类型的变量分配List

不变(Invariance):这意味着,你只能使用原始指定的类型。固定泛型类型参数既不是协变类型,也不是逆变类型。你无法向List类型的变量分配List(Of Base)。


阅读全文

2019-07-06 Java语法糖-泛型-通配符

       在范型的使用上,问号”?”表示通配符,代表运行时未知类型。通配符可用作type parameter:

  • 方法形参
  • 成员变量
  • 局部变量
  • 方法返回值(不建议这么做,方法的返回值应该是明确的)

       通配符不能用作泛型方法的实参,不能用于范型类的初始化,即不能用作type argument。


1. 有界通配符

       有界通配符与有界泛型一样,代表着泛型的某个边界。不同的地方在于,有界通配符除了上界之外,还有下界,通过super关键字实现。


1.1 上界通配符

       利用“? extends UpperBoundClass”可以实现上界通配符,比如你想实现一个工具方法,对一个数字列表进行求和,可以将方法参数声明为: <? extends Number>:

public static double sumOfList1(List<? extends Number> list) {
    double s = 0.0;
    for (Number n : list) {
        s += n.doubleValue();
    }
    return s;
}

代码清单1

阅读全文

2019-07-05 Java语法糖-泛型-泛型边界

1. 有界泛型

1.1 有界泛型的定义

       当我们需要一个工具类只用于做数值计算,或者只用于做集合的遍历,方法只希望接收Number或者Collection,不希望接受其他类型,否则就得写一大堆instanceof的if判断。 为了解决这个问题,我们需要定义泛型的上边界,即方法所能接收的层级最高的类型。

       在Java中,用extends关键字可以实现有界泛型的定义。格式为:<T extends UpperBoundClass>,其中UpperBoundClass为上边界类型。代码实例如下所示:

public static <T extends Number> void computeNumber(T number1, T number2) {
    //compute
}

public static <T extends Collection> void iter(T collection) {
    //iterate
}

代码清单1


       在使用代码清单1的工具方法的时,可以这么使用:

//compile success
Integer number1 = 0;
BigDecimal number2 = new BigDecimal("2");
computeNumber(number1, number2);
iter(new ArrayList<>(Arrays.asList("1", "2", "a")));
iter(new HashSet<>(Arrays.asList("1", "2", "a")));

//compile fail: Wrong 1st argument type
computeNumber("string1", number2);

代码清单2

阅读全文

2019-07-04 Java语法糖-泛型-类型擦除

       在Java的字节码文件中,只存在普通的类、接口或者方法。泛型擦除不会引入新的类,所以不会产生额外的运行时开销Java编译器将会对泛型代码进行以下擦除逻辑:

  • 如果泛型不存在类型边界,则使用Object类替代泛型。
  • 如果泛型存在类型边界,则使用边界的类型替代泛型。
  • 编译器自动加入类型转换代码。
  • 存在继承关系的泛型类,编译器将生成桥接方法,在类型擦除过后依然保留多态特性。


代码示例


       泛型类和泛型方法的擦除逻辑一致,我们以泛型类为例进行说明,不再赘述泛型方法的逻辑。


1. 无类型边界的泛型类的擦除

       我们定义一个泛型测试类:

public class GenericClassErasureTest {

    public static void main(String[] args) {
        Node<String> node = new Node<>("tail", null);
        System.out.println(node.getData());
    }

    public static class Node<T> {
        private T data;
        private Node<T> next;

        public Node(T data, Node<T> next) {
            this.data = data;
            this.next = next;
        }

        public T getData() { return data; }
    }
}

代码清单generic-type-erasure1

阅读全文

2019-07-02 Java语法糖-泛型

前言


       JDK自1.5版本引入泛型机制,代码中常用的容器类(如java.util.Collection和java.util.Map)都属于泛型类。泛型的引入带来了以下好处:

  • 在使用集合类时,将以往JDK版本的类型错配报错,由运行时提前到了编译期,使得代码不再需要处理ClassCastException。
  • 在访问泛型元素时,不再需要强制转换对象类型,代码更加简洁优雅。
  • 泛型提高了代码复用率。


c compile

图片来源


       由于Java泛型属于语法糖,编译过后会将泛型信息擦除。相较于C++和C#的泛型,Java的泛型更像是伪泛型,因为JVM中没有泛型的概念,完全是编译器的magic。

阅读全文