Java学习笔记————泛型程序设计

1.  泛型程序设计

1.1      泛型的引出

要求设计一个可以表示坐标的类(X,Y)但是此坐标要可以同时满足以下三种集中要求:

·X=10、Y=100        ·X=10.3、Y=50.2     ·X=”东经180°”、Y=”北纬210°”

分析:

因为现在的程序中需要接受三种数据类型的数据,所以呢为了保证程序的正确性,最好使用Object类完成,因为Object可以接受任意的引用数据类型:

·数字 à 自动装箱操作 à Object       ·字符串 à Object

按照以上的特点,完成程序:

class Point {

    private Object x;

    private Object y;

    public Object getX() {

        return x;

    }

    public void setX(Object x) {

        this.x = x;

    }

    public Object getY() {

        return y;

    }

    public void setY(Object y) {

        this.y = y;

    }

}

此时Point类完成了,那么设置一个整形数字,来观察是否可以操作。

public class GenDemo01 {

    public static void main(String[] args) {

        Point p = new Point();

        p.setX(10); // 设置坐标。int --> Integer --> Object

        p.setY(10);// 设置坐标。int --> Integer --> Object

        int x = (Integer)p.getX() ; // 取出X:Object --> Integer --> int

        int y = (Integer)p.getY() ; // 取出X:Object --> Float --> float

        System.out.println("x = " + x);

        System.out.println("y = " + y);

    }

}

下面继续完成设置小数的操作:

public class GenDemo02 {

    public static void main(String[] args) {

        Point p = new Point();

        p.setX(10.3f); // 设置坐标。float --> Float --> Object

        p.setY(10.6f);// 设置坐标。float --> Float --> Object

        float x = (Float)p.getX() ; // 取出X:Object --> Float --> float

        float y = (Float)p.getY() ; // 取出X:Object --> Float --> float

        System.out.println("x = " + x);

        System.out.println("y = " + y);

    }

}

下面继续设置字符串最为X和Y的坐标:

public class GenDemo03 {

    public static void main(String[] args) {

        Point p = new Point();

        p.setX("东经120度"); // 设置坐标。String --> Object

        p.setY("北纬230度");// 设置坐标。String --> Object

        String x = (String)p.getX() ;   // 取出X:Object --> String

        String y = (String)p.getY() ;   // 取出X:Object --> String

        System.out.println("x = " + x);

        System.out.println("y = " + y);

    }

}

此时基本点的功能已经完成了,但是此种操作是否存在问题呢???

在此操作之中,可以发现所有的内容都是Object进行操作的,那么就意味着,可以设置任意的类型,即:X可以使整型,Y可以使字符串类型。

public class GenDemo04 {

    public static void main(String[] args) {

        Point p = new Point();

        p.setX(10); // 设置坐标。int --> Integer --> Object

        p.setY("北纬230度");// 设置坐标。int --> Integer --> Object

        int x = (Integer)p.getX() ; // 取出X:Object --> Integer --> int

        int y = (Integer)p.getY() ; // 取出X:Object --> Float --> float

        System.out.println("x = " + x);

        System.out.println("y = " + y);

    }

}

此时程序编译的时候不存在任何问题,但是在执行的时候却出现了以下异常。

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

    at com.java.genericsdemo01.GenDemo04.main(GenDemo04.java:20)

之所以会这样,是因为在程序中所有的属性都可以向Object进行转换,那么,此程序的入口就显得不那么规范了,而且存在安全漏洞。

但是从之前所学过的全部代码来看,此处孩子能应用到这里了。没有更好的方法了

1.2      泛型<1>基础知识

JDK 1.5 之后出现了新的技术 —— 泛型,此技术的最大特点就是类中的属性的类型可以由外部决定,而且声明的时候应该采取如下形式:

class 类名称 <泛型类型,泛型类型,……>{

}

那么现在采用如上的格式来修之前的操作。

package com.java.genericsdemo02;

public class Point<T> { // 表示坐标类

    private T x;    //  X 的属性类型有外部决定

    private T y;    //  Y 的属性类型由外部决定

    public T getX() {

        return x;

    }

    public void setX(T x) {

        this.x = x;

    }

    public T getY() {

        return y;

    }

    public void setY(T y) {

        this.y = y;

    }

}

此时,程序中加入了泛型之后,可以发现一切操作类型都不在由程序固定设置。而是在实例化对象的时候在外部进行了指定。

package com.java.genericsdemo02;

public class GenDemo05 {

    public static void main(String[] args) {

        Point<Integer> p = new Point<Integer>();

        p.setX(10); // 设置坐标。int --> Integer --> Object

        p.setY(20);// 设置坐标。int --> Integer --> Object

        System.out.println("x = " + p.getX());

        System.out.println("y = " + p.getY());

    }

}

发现此时在使用Point类的时候,需要加入一个属性类型的声明。而且加入之后再取出属性值的时候本身也变得非常容易,不需要再使用向下转型了。

而且,使用上面的操作有一点点最方便之处,如果此时设置的内容不是整型,那么程序中将出现错误。

package com.java.genericsdemo02;

public class GenDemo06 {

    public static void main(String[] args) {

        Point<Integer> p = new Point<Integer>();

        p.setX(10); // 设置坐标。int --> Integer --> Object

        p.setY[1]("北纬230度");// 设置坐标。int --> Integer --> Object

        System.out.println("x = " + p.getX());

        System.out.println("y = " + p.getY());

    }

}

加入泛型之后可以对程序的操作起到更加安全的目的.

1.3      泛型<2>注意点

在使用泛型操作的时候,实际上有很多小的注意点,例如:构造方法上依然可以使用泛型或者有一种可以成为泛型的擦除。

1.3.1        在构造方法上引用泛型

一般在开发中,经常使用造方法设置属性的内容,那么此时实际上依然可以使用泛型的类型。

package com.java.demo03;

public class Point<T> {

    private T x;

    private T y;

    public Point(T x, T y) {

        this.setX(x);

        this.setY(y);

    }

    public T getX() {

        return x;

    }

    public void setX(T x) {

        this.x = x;

    }

    public T getY() {

        return y;

    }

    public void setY(T y) {

        this.y = y;

    }

}

那么此时在调用的时候就需要使用构造方法设置内容,当然,设置的内容依然由泛型本身决定。

package com.java.demo03;

public class GenDemo07 {

    public static void main(String[] args) {

        Point<Integer> p = new Point<Integer>(10, 20);

        int x = p.getX();

        int y = p.getY();

        System.out.println("x = " + x);

        System.out.println("y = " + y);

    }

}

1.3.2        擦除泛型

如果在使用的时候没有指定泛型类型的话,则表示擦除泛型类型。

泛型一旦擦除之后,就按照Object进行接收,以保证程序不发生任何错误。

package com.java.demo03;

public class GenDemo08 {

    public static void main(String[] args) {

        Point p = new Point(10, 20);

        int x = (Integer) p.getX();

        int y = (Integer) p.getY();

        System.out.println("x = " + x);

        System.out.println("y = " + y);

    }

}

但是在以上的操作代码中,依然会存在警告信息。那么应该如何去掉警告信息呢???

package com.java.demo03;

public class GenDemo08 {

    public static void main(String[] args) {

        Point<Object> p = new Point<Object>(10, 20);

        int x = (Integer) p.getX();

        int y = (Integer) p.getY();

        System.out.println("x = " + x);

        System.out.println("y = " + y);

    }

}

但是,以上的操作虽然去掉了警告信息,但是有些多余,建议在开发者一般不要擦除泛型。

1.4      通配符

在泛型的操作中通配符的使用较多,而且在日后的系统类库中有很多地方都要使用这些操作

例如,现在又如下代码:

package com.java.demo04;

public class Test {

    public static void main(String[] args) {

        Object obj ="Hello World!!!";

    }

}

以上的语法实际上是进行了向上的转型,因为String 是 Object的子类,但是在泛型中却没有此概念。

1.4.1        ?

在进行对象转型的时候可以使用自动的向上转型,但是在使用泛型的时候却没有此种操作。

package com.java.demo04;

public class Point<T> {

    private T x;

    private T y;

    public T getX() {

        return x;

    }

    public void setX(T x) {

        this.x = x;

    }

    public T getY() {

        return y;

    }

    public void setY(T y) {

        this.y = y;

    }

}

那么下面定义两个Point类的对象。

package com.java.demo04;

public class GenDemo09 {

    public static void main(String[] args) {

        Point<Object> p1 = new Point<Object>();

        Point<Integer> p2 = new Point<Integer>();

        p1 = p2;    //  此时根本无法进行转换。

    }

}

此时的程序根本就无法进行转换的操作,此时程序实际上就已经不完全属于对象转换操作了,而属于一个大的类型和小的类型划分。

例如:将“Point<Object> p1 = new Point<Object>();”表示为商场的全部商品,而“Point<Integer> p2 = new Point<Integer>();”表示每一个顾客购买的商品,如果现在执行“p1 = p2;”那么就意味着,此顾客所购买的商品就是商场的全部商品,这样肯定说不通,所以说不能接收。

不能使用以上的方式接收最大的影响在于方法的参数接收。

package com.java.demo04;

public class GenDemo10 {

    public static void main(String[] args) {

        Point<Object> p1 = new Point<Object>();

        Point<Integer> p2 = new Point<Integer>();

        fun(p1);

        fun(p2);

    }

    public static void fun(Point point){    //  表示此时可以接受任意的类型。

        System.out.println(point.getX());

        System.out.println(point.getY());

    }

}

此时程序可以正常编译运行,但是“fun(Point point)”处会出现警告信息,现在不想让其出现警告信息,那么可以进行以下操作:

package com.java.demo04;

public class GenDemo10 {

    public static void main(String[] args) {

        Point<Object> p1 = new Point<Object>();

        Point<Integer> p2 = new Point<Integer>();

        fun(p1);

        fun(p2);

    }

    public static void fun(Point<?> point){ //  表示此时可以接受任意的类型。

        System.out.println(point.getX());

        System.out.println(point.getY());

    }

}

程序中的“?”表示可以接收任意类型的泛型类型,但是只能是接收和输出,并不能修改。

1.4.2        泛型的上限

上限就是指一个操作泛型的最大的操作父类,例如,现在最大的上限设置成“Number”那么此时,所能够就收的类型只能是Number及其子类(Integer)。

泛型上限通过以下语法完成:

? extends 类

例如:在Point类中只能设置数字类型的坐标。

package com.java.demo05;

public class Point<T extends Number> {  //  最高只能是Number类型。

    private T x;

    private T y;

    public T getX() {

        return x;

    }

    public void setX(T x) {

        this.x = x;

    }

    public T getY() {

        return y;

    }

    public void setY(T y) {

        this.y = y;

    }

}

以上的泛型类型明确的指出,最大的操作父类是Number,设置的内容只能是Integer、Float等等。

package com.java.demo05;

public class GemDemo11 {

    public static void main(String[] args) {

        Point<Integer> p1 = new Point<Integer>();   //  设置的是Number的子类。

    }

}

如果此时设置的泛型类型是字符串的话,则也会出现错误。

package com.java.demo05;

public class GemDemo12 {

    public static void main(String[] args) {

        Point<String> p1 = new Point<String>(); //  设置的是Number的子类。

    }

}

Bound mismatch: The type String is not a valid substitute for the bounded parameter <T extends Number> of the type Point<T>

而且泛型上限也可以在方法上使用,例如,接收参数:

package com.java.demo05;

public class GenDemo13 {

    public static void main(String[] args) {

        Point<Integer> p2 = new Point<Integer>();

        fun(p2);

    }

    public static void fun(Point<? extends Number> point){  //此时可以接受Number的子类。

        System.out.println(point.getX());

        System.out.println(point.getY());

    }

}

1.4.3        泛型的下限

泛型的下限指的是只能设置其具体的类或者父类,设置语法如下:

? super 类

例如,定义一个方法,此方法之鞥接收String或Object类型的泛型对象。

package com.java.demo06;

public class Point<T> {

    private T x;

    private T y;

    public T getX() {

        return x;

    }

    public void setX(T x) {

        this.x = x;

    }

    public T getY() {

        return y;

    }

    public void setY(T y) {

        this.y = y;

    }

}

在方法中设置泛型的下限:

package com.java.demo06;

public class GenDemo14 {

    public static void main(String[] args) {

        Point<String> p1 = new Point<String>();

        Point<Object> p2 = new Point<Object>();

        fun(p1);

        fun(p2);

    }

    public static void fun(Point<? super String> point){

        System.out.println(point.getX());

        System.out.println(point.getY());

    }

}

1.5      泛型接口

泛型不管可以在类上使用,还可以在接口中进行定义。操作的语法如下:

Interface 类口名称<泛型类型,泛型类型,…>{}

范例:定义泛型接口:

package com.java.demo07;

public interface Demo<T> {  //  定义泛型接口。

    public void print(T param); //  此抽象方法中使用了泛型类型

}

泛型接口定义完成之后,下面就需要定义类是吸纳接口,实现的方法有两种:

范例:第一种实现手段

package com.java.demo07;

public class DemoImpl01<T> implements Demo<T> {

    public void print(T param) {

        System.out.println("Param = " + param);

    }

}

下面对以上的程序进行测试:

package com.java.demo07;

public class GenDemo15 {

    public static void main(String[] args) {

        Demo<String> demo = new DemoImpl01<String>();

        demo.print("Hello");

    }

}

范例:第二种做法(设置具体类型)

package com.java.demo07;

public class DemoImpl02 implements Demo<DemoImpl02> {   //  设置具体类型

    public void print(DemoImpl02 param) {

        System.out.println("Param = " + param);

    }

}

此时print方法中只能接收DemoImpl02实例对象

package com.java.demo07;

public class GenDemo16 {

    public static void main(String[] args) {

        Demo<DemoImpl02> demo = new DemoImpl02();

        demo.print(new DemoImpl02());

    }

}

1.6      泛型方法

泛型除了可以再类中定义之外,还可以在方法中定义,而且在方法上使用泛型,此方法所在的类不一定是泛型的操作。

package com.java.demo08;

public class Demo {

    public <T> T print(T param){    //  定义泛型方法

        return param;

    }

}

Demo类中print()方法可以接收泛型类型,而且此方法的返回值也是指定的泛型类型,下面使用以上的类进行操作。

package com.java.demo08;

public class GenDemo17 {

    public static void main(String[] args) {

        Demo demo = new Demo();

        System.out.println(demo.print(1));

    }

}

当然也可以将方法的返回值定义成一个泛型的数组。

package com.java.demo08;

public class GenDemo18 {

    public static void main(String[] args) {

        Integer i[] = fun(1, 2, 3, 4, 5, 6, 7, 8, 9);

        for(int x : i){

            System.out.print(x + "\t");

        }

    }

    public static <T> T[] fun(T... param) {

        return param;   //  返回数组

    }

}

1.7      泛型的嵌套操作

现在只是突出语法,具体操作的意义要等到后面学习到类库才能够更加明白。

package com.java.demo09;

public class Info<T> {

    private T param;

    public T getParam() {

        return param;

    }

    public void setParam(T param) {

        this.param = param;

    }

}

之后再定义一个Person类

package com.java.demo09;

public class Person<T> {

    private T info;

    public T getInfo() {

        return info;

    }

    public void setInfo(T info) {

        this.info = info;

    }

}

此时如果要将Info的类型设置到Person中,那么同时既要指定Person的泛型类型,又要指定Info中的泛型类型。

package com.java.demo09;

public class Test {

    public static void main(String[] args) {

        Person<Info<String>> person = new Person<Info<String>>();

        person.setInfo(new Info<String>());

        person.getInfo().setParam("Hello World!!!");

        System.out.println(person.getInfo().getParam());

    }

}

这就是泛型的嵌套的设置,以上的在后面将会有所应用。

1.8      泛型的操作范例

现在有如下的题目要求:

要求设计一个程序,定义一个Person类,Person类中药存放具体的信息,但是信息分为基本信息或者联系方式等等,那么此时该如何设计呢?

此时最好的设计就是需要定义一个好事信息的操作标准,但是此时这个标准肯定使用接口实现,但是现在接口中并不编写任何的操作。

package com.java.demo10;

public interface Info {

}

接口中没有任何操作代码,所以,此种接口在设计上称为标示接口,标示一种能力。之后定义Person类,Person类中的信息只能由Info的子类决定,所以此时指定了上限。

package com.java.demo10;

public class Person<T extends Info> {

    private T info;

    public T getInfo() {

        return info;

    }

    public void setInfo(T info) {

        this.info = info;

    }

}

以上的操作类中,能够设置的内容只能是Info的子类.

package com.java.demo10;

public class Basic implements Info {

    private String name;

    private int age;

    public Basic() {

        super();

    }

    public Basic(String name, int age) {

        super();

        this.name = name;

        this.age = age;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    public String toString() {

        return "人的信息:" + "\n\t|-姓名:" + this.getName() + "\n\t|-年龄:" + this.getAge();

    }

}

以上只是基本信息,但是在人中还有联系方式的子类.

package com.java.demo10;

public class Contact implements Info {

    private String address;

    private String zipcode;

    public Contact() {

        super();

    }

    public Contact(String address, String zipcode) {

        super();

        this.address = address;

        this.zipcode = zipcode;

    }

    public String getAddress() {

        return address;

    }

    public void setAddress(String address) {

        this.address = address;

    }

    public String getZipcode() {

        return zipcode;

    }

    public void setZipcode(String zipcode) {

        this.zipcode = zipcode;

    }

    public String toString() {

        return "联系信息:" + "\n\t|-家庭住址:" + this.getAddress() + "\n\t|-邮政编码:"

                + this.getZipcode();

    }

}

下面使用基本信息完成操作:

package com.java.demo10;

public class TestPerson01 {

    public static void main(String[] args) {

        Person<Basic> person = new Person<Basic>();

        person.setInfo(new Basic("张三", 30));

        System.out.println(person.getInfo());

    }

}

下面使用地址信息完成操作:

package com.java.demo10;

public class TestPerson02 {

    public static void main(String[] args) {

        Person<Contact> person = new Person<Contact>();

        person.setInfo(new Contact("开封市", "475001"));

        System.out.println(person.getInfo());

    }

}

以上Person中的信息只能是Info的子类,从而保证了操作的正确性.



[1] 下划线表示有错误出现

原文地址:https://www.cnblogs.com/zhangtingkuo/p/2785002.html