向上类型转换

Object object1 = 1;
Object object2 = new RuntimeException();

向下类型转换

class Animal {}
class HumanBeing extends Animal {}
class Male extends HumanBeing {}
class Female extends HumanBeing {}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Female();
        // 向下类型转换成功
        HumanBeing humanBeing = (HumanBeing) animal;
        Female femal = (Female) animal;

        // 向下类型转换失败
        Male male = (Male) animal;
    }
}

泛型类

泛型的本质是参数化类型。在泛型编程中,不仅数据的值 可以通过参数传递;数据的类型 也可以通过参数传递。
对于传值参数,使用圆括号包围起来,比如(int x, double y)。
对于类型参数,使用尖括号包围起来,比如<K, V>。
值得注意的是:类型参数只能是引用类型,不能是基本数据类型
定义泛型类的语法是:

class ClassName<GenericParam1, GenericParam2> extends Object {
}

在实例化泛型类的时候,必须指定类型参数,也就是给类型参数传值。否则,就会引起类型擦除。(因为只有创建泛型类对象的时候,才会指定类型参数,所以在类的静态成员中,是无法使用泛型类的类型参数的。
java-generic-example-1.png


泛型方法

泛型方法既可以是静态的,也可以是实例的,既可以在泛型类中,也可以在非泛型类中。定义泛型方法的语法:

public static <T> T getData() {
    return data;
}

在调用泛型方法时,不需要为类型参数传值,因为编译器会自动根据参数,得到具体的类型。所以泛型方法除了定义和普通方法不同,其他方面没什么两样。


泛型接口

package cn.timd;

interface Generic<T> {
    T getData();
}

class GenericClass<T> implements Generic<T> {
    private T data;

    public GenericClass(T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }
}

public class Main {
    public static void main(String[] args) {
        GenericClass<Integer> genericClass = new GenericClass<Integer>(3);
        Integer data = genericClass.getData();
    }
}

泛型数组

不能直接创建泛型数组,但是可以通过以下方式实现泛型数组:
java-generic-example-2.png


类型擦除

在使用泛型时,如果没指定数据类型,就会发生类型擦除:编译器为了避免报错,会将所有数据向上转型成Object,(使用时,需要进行强制的向下类型转换)。


限制泛型的类型

package cn.timd;

class MyNumber<T extends Number> {
    public T getMax(T[] array) {
        T max = null;
        for (T element: array)
            if (max == null || element.doubleValue() > max.doubleValue())
                max = element;

        return max;
    }
}

public class Main {
    public static void main(String[] args) {
        Integer[] array = new Integer[]{3, 5, 1, 2};
        System.out.println(new MyNumber<Integer>().getMax(array));
    }
}

子类和子类型

翻译自:http://www.cs.princeton.edu/courses/archive/fall98/cs441/mainus/node12.html
原文:

There are important differences between subtypes and subclasses in supporting reuse. Subclasses allow one to reuse the code inside classes - both instance variable declarations and method definitions. Thus they are useful in supporting code reuse inside a class. Subtyping on the other hand is useful in supporting reuse externally, giving rise to a form of polymorphism. That is, once a data type is determined to be a subtype of another, any function or procedure that could be applied to elements of the supertype can also be applied to elements of the subtype. Notice that the subtype relation depends only on the public interfaces of objects, not their implementations. In particular if one type is a subtype of another, it is not necessary for objects of those types to have arisen from classes that are in the subclass relation. By the same argument, objects of the same type need not have arisen from the same class. They may have been generated by classes with different collections of (hidden) instance variables and method bodies, but whose (visible) methods have the same types. This is similar to the impact of information hiding in programming languages which support abstract data types. The paper [CHC90] was one of the first to examine typing problems in statically-typed object-oriented languages. One of the key points made in that paper (as reflected in the title) is this distinction between inheritance and subtyping. Nevertheless, we note that most existing object-oriented programming languages do identify type and class as well as subtype and subclass. That is, a class is identified with the type of objects it generates, while only subclasses generate subtypes. (It is worth noting that some languages, like C++, support a form of private inheritance which does not result in subtypes.) The new language Java provides more separation of types from classes than is usual by providing interfaces which are distinct from the classes which may conform to them. We believe that it is unfortunate to identify subtyping and inheritance, as types should only refer to the interface of an object, not its implementation. While the analysis of typing in the rest of this paper can be profitably applied to languages which do identify subclass and subtype, our analysis also shows how object-oriented languages could profit from the separation of these notions.

子类型和子类在支持重用方面有很多重要的不同之处。子类可以重用父类内部的代码(包括实例变量的声明 和 方法的定义)。因此子类在复用一个类内部代码的时候,非常有用。而子类型是从外部来支持重用,并且由子类型引出了多态的一种形式。也就说,如果一个数据类型是另外一个数据类型的子类型,那么任何能用应用到这个子类型的方法或过程,也能应用到其它子类型。
子类型关系依赖对象的公用接口,而非他们的实现。当一个类型是另外一个类型的子类型的时候,这些类型的对象没必要 从具有子类关系的类中 生成。 对于某个参数,传递给它的对象无需从相同的类来生成。它们可能是通过 私有接口不同,但是对外接口相同的类 生成的。这与支持抽象数据类型的编程语言中,信息隐藏的作用类似。
在Java中,实现了相同接口的类都是接口的子类型,而继承了某个基类的类都是基类的子类,因此区分子类型和子类的一个方式是:子类型只继承了接口,没有继承实现。

设类B继承自类A,G是一个泛型类,则:

比如:
java-generic-example-3.png