Java中的泛型<T>类与方法

泛型就是变量类型的参数化。在使用泛型前,存入集合中的元素可以是任何类型的,当从集合中取出时,所有的元素都是Object类型,需要进行向下的强制类型转换,转换到特定的类型。而强制类型转换容易引起运行时错误。

泛型类型参数只能被类或接口类型赋值,不能被原生数据类型赋值,原生数据类型需要使用对应的包装类。

例子一:使用了泛型


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

class Test<T> {

    private T ob; // 定义泛型成员变量

    public Test(T ob) {

        this.ob = ob;

    }

    public T getOb() {

        return ob;

    }

    public void setOb(T ob) {

        this.ob = ob;

    }

    public void showType() {

        System.out.println("T的实际类型是: " + ob.getClass().getName());

    }

}

public class TestDemo {

    public static void main(String[] args) {

        // 定义泛型类Gen的一个Integer版本

        Test<Integer> intOb = new Test<Integer>(88);

        intOb.showType();

        int i = intOb.getOb();

        System.out.println("value= " + i);

        System.out.println("----------------------------------");

        // 定义泛型类Gen的一个String版本

        Test<String> strOb = new Test<String>("Hello Gen!");

        strOb.showType();

        String s = strOb.getOb();

        System.out.println("value= " + s);

    }

例子二:没有使用泛型


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

class Test2{

    private Object ob; // 定义一个通用类型成员

    public Test2(Object ob) {

        this.ob = ob;

    }

    public Object getOb() {

        return ob;

    }

    public void setOb(Object ob) {

        this.ob = ob;

    }

    public void showTyep() {

        System.out.println("T的实际类型是: " + ob.getClass().getName());

    }

}

public class TestDemo2 {

    public static void main(String[] args) {

        // 定义类Gen2的一个Integer版本

        Test2 intOb = new Test2(new Integer(88));

        intOb.showTyep();

        int i = (Integer) intOb.getOb();

        System.out.println("value= " + i);

        System.out.println("---------------------------------");

        // 定义类Gen2的一个String版本

        Test2 strOb = new Test2("Hello Gen!");

        strOb.showTyep();

        String s = (String) strOb.getOb();

        System.out.println("value= " + s);

    }

}

运行结果:

两个例子运行Demo结果是相同的,控制台输出结果如下:

T的实际类型是:

java.lang.Integer

value= 88

----------------------------------

T的实际类型是: java.lang.String

value= Hello Gen!

Process finished with exit code 0

看明白这个,以后基本的泛型应用和代码阅读就不成问题了。

深入泛型

深入泛型

原始代码

有两个类如下,要构造两个类的对象,并打印出各自的成员x。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

public class StringDemo {

    private String x;

    public StringDemo(String x) {

        this.x = x;

    }

    public String getX() {

        return x;

    }

    public void setX(String x) {

        this.x = x;

    }

}

public class DoubleDemo {

    private Double x;

    public DoubleDemo(Double x) {

        this.x = x;

    }

    public Double getX() {

        return x;

    }

    public void setX(Double x) {

        this.x = x;

    }

}

重构

因为上面的类中,成员和方法的逻辑都一样,就是类型不一样,因此考虑重构。Object是所有类的父类,因此可以考虑用Object做为成员类型,这样就可以实现通用了,实际上就是“Object泛型”,暂时这么称呼。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public class ObjectDemo {

    private Object x;

    public ObjectDemo(Object x) {

        this.x = x;

    }

    public Object getX() {

        return x;

    }

    public void setX(Object x) {

        this.x = x;

    }

}

写出Demo方法如下:


1

2

3

4

5

6

7

8

9

10

public class ObjectDemoDemo {

    public static void main(String args[]) {

        ObjectDemo strDemo = new ObjectDemo(new StringDemo("Hello Generics!"));

        ObjectDemo douDemo = new ObjectDemo(new DoubleDemo(new Double("33")));

        ObjectDemo objDemo = new ObjectDemo(new Object());

        System.out.println("strFoo.getX=" + (StringDemo) strDemo.getX());

        System.out.println("douFoo.getX=" + (DoubleDemo) douDemo.getX());

        System.out.println("objFoo.getX=" + objDemo.getX());

    }

}

运行结果如下:

strDemo.getX=String[email protected]

douDemo.getX=Double[email protected]

obj[email protected]

解说:在Java 5之前,为了让类有通用性,往往将参数类型、返回类型设置为Object类型,当获取这些返回类型来使用时候,必须将其“强制”转换为原有的类型或者接口,然后才可以调用对象上的方法。

实现

强制类型转换很麻烦,我还要事先知道各个Object具体类型是什么,才能做出正确转换。否则,要是转换的类型不对,比如将“Hello Generics!”字符串强制转换为Double,那么编译的时候不会报错,可是运行的时候就挂了。那有没有不强制转换的办法----有,改用 Java5泛型来实现。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

class GenericsTest<T> {

    private T x;

    public GenericsTest(T x) {

        this.x = x;

    }

    public T getX() {

        return x;

    }

    public void setX(T x) {

        this.x = x;

    }

}

public class GenericsTestDemo {

    public static void main(String args[]) {

        GenericsTest<String> strFoo = new GenericsTest<String>("Hello Generics!");

        GenericsTest<Double> douFoo = new GenericsTest<Double>(new Double("33"));

        GenericsTest<Object> objFoo = new GenericsTest<Object>(new Object());

        System.out.println("strTest.getX=" + strTest.getX());

        System.out.println("douTest.getX=" + douTest.getX());

        System.out.println("objTest.getX=" + objTest.getX());

    }

}

运行结果:

strTest.getX=Hello Generics!

douTest.getX=33.0

obj[email protected]

和使用“Object泛型”方式实现结果的完全一样,但是这个Demo简单多了,里面没有强制类型转换信息。

下面解释一下上面泛型类的语法:

使用<T>来声明一个类型持有者名称,然后就可以把T当作一个类型代表来声明成员、参数和返回值类型。

当然T仅仅是个名字,这个名字可以自行定义。

class GenericsTest<T> 声明了一个泛型类,这个T没有任何限制,实际上相当于Object类型,实际上相当于 class GenericsTest<T extends Object>。

与Object泛型类相比,使用泛型所定义的类在声明和构造实例的时候,可以使用“<实际类型>”来一并指定泛型类型持有者的真实类型。类如

GenericsTest<Double> douTest=new GenericsTest<Double>(new Double("33"));

当然,也可以在构造对象的时候不使用尖括号指定泛型类型的真实类型,但是你在使用该对象的时候,就需要强制转换了。比如:GenericsTest douTest=new GenericsTest(new Double("33"));

实际上,当构造对象时不指定类型信息的时候,默认会使用Object类型,这也是要强制转换的原因.


高级应用

限制泛型

在上面的例子中,由于没有限制class GenericsTest<T>类型持有者T的范围,实际上这里的限定类型相当于Object,这和“Object泛型”实质是一样的。限制比如我们要限制T为集合接口类型。只需要这么做:

class GenericsTest<T extends Collection>,这样类中的泛型T只能是Collection接口的实现类,传入非Collection接口编译会出错。

注意:<T extends Collection>这里的限定使用关键字extends,后面可以是类也可以是接口。但这里的extends已经不是继承的含义了,应该理解为T类型是实现Collection接口的类型,或者T是继承了XX类的类型。

下面继续对上面的例子改进,我只要实现了集合接口的类型:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public class CollectionGenTest<T extends Collection> {

    private T x;

    public CollectionGenTest(T x) {

        this.x = x;

    }

    public T getX() {

        return x;

    }

    public void setX(T x) {

        this.x = x;

    }

}

实例化的时候可以这么写:


1

2

3

4

5

6

7

8

9

10

11

12

public class CollectionGenTestDemo {

    public static void main(String args[]) {

        CollectionGenTest<ArrayList> listTest= null;

        listTest = new CollectionGenTest<ArrayList>(new ArrayList());

   

        // 需要将CollectionGenTest<Collection>改为CollectionGenTest<ArrayList>

        // CollectionGenTest<Collection> listTest = null;

        // listTest=new CollectionGenTest<ArrayList>(new ArrayList());

        System.out.println("实例化成功!");

    }

}

当前看到的这个写法是可以编译通过,并运行成功。可是注释掉的两行加上就出错了,因为<T
extends
Collection>这么定义类型的时候,就限定了构造此类实例的时候T是确定的一个类型,这个类型实现了Collection接口,但是实现
Collection接口的类很多很多,如果针对每一种都要写出具体的子类类型,那也太麻烦了,我干脆还不如用Object通用一下。别急,泛型针对这种情况还有更好的解决方案,那就是“通配符泛型”。

多接口限制

虽然Java泛型简单的用 extends 统一的表示了原有的 extends 和
implements 的概念,但仍要遵循应用的体系,Java 只能继承一个类,但可以实现多个接口,所以你的某个类型需要用 extends
限定,且有多种类型的时候,只能存在一个是类,并且类写在第一位,接口列在后面,也就是:

<T extends SomeClass & interface1 & interface2 & interface3>

这里的例子仅演示了泛型方法的类型限定,对于泛型类中类型参数的限制用完全一样的规则,只是加在类声明的头部,如:


1

2

3

public class Demo<T extends Comparable & Serializable> {

    // T类型就可以用Comparable声明的方法和Seriablizable所拥有的特性了

}

通配符泛型

为了解决类型被限制死了不能动态根据实例来确定的缺点,引入了“通配符泛型”,针对上面的例子,使用通配泛型格式为<? extends Collection>,“?”代表未知类型,这个类型是实现Collection接口。那么上面实现的方式可以写为:


1

2

3

4

5

6

7

8

9

10

11

public class CollectionGenTestDemo {

    public static void main(String args[]) {

        CollectionGenTest<ArrayList> listTest = null;

        listTest= new CollectionGenTest<ArrayList>(new ArrayList());

     

        // CollectionGenTest<Collection> listTest = null;

        // listTest=new CollectionGenTest<ArrayList>(new ArrayList());

        System.out.println("实例化成功!");

    }

}

注意:

1、如果只指定了<?>,而没有extends,则默认是允许Object及其下的任何Java类了。也就是任意类。

2、通配符泛型不单可以向下限制,如<? extends Collection>,还可以向上限制,如<? super Double>,表示类型只能接受Double及其上层父类类型,如Number、Object类型的实例。

3、泛型类定义可以有多个泛型参数,中间用逗号隔开,还可以定义泛型接口,泛型方法。这些都与泛型类中泛型的使用规则类似。

泛型方法

编辑

是否拥有泛型方法,与其所在的类是否泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值前。如:


1

2

3

4

5

6

7

8

9

10

11

12

13

public class ExampleA {

    public <T> void f(T x) {

        System.out.println(x.getClass().getName());

    }

    public static void main(String[] args) {

        ExampleA ea = new ExampleA();

        ea.f(" ");

        ea.f(10);

        ea.f(‘a‘);

        ea.f(ea);

    }

}

输出结果:

java.lang.String

java.lang.Integer

java.lang.Character

ExampleA

使用泛型方法时,不必指明参数类型,编译器会自己找出具体的类型。泛型方法除了定义不同,调用就像普通方法一样。

需要注意,一个static方法,无法访问泛型类的类型参数,所以,若要static方法需要使用泛型能力,必须使其成为泛型方法。

比如:

public class Demo{

public static <T> T test(T a){

return a;

}

}

参考资料:

http://baike.baidu.com/link?url=JgPvpMZUrPNh9ymtoV6tL4ZcWSRcIKFtYQMc6_c3ms6QU-vlofuaN58OpZCvIuG2uO5FpkgIbvqM1VyfMhhMvK

原文地址:https://www.cnblogs.com/qiyunhua/p/8337163.html

时间: 2024-10-13 11:33:41

Java中的泛型<T>类与方法的相关文章

javaweb part3 ----HTTP协议详解,以及java中request response的类以及方法

HTTP协议详解Requsert Headers 浏览器 向服务器 发送的 请求信息Get /Myweb/persons.html HTTP/1.1 (必须)Host:localhost:8080Connection:keep-alive Accept支持格式:Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8User-Agent:兼容浏览器的版本Accept-

Java 中的泛型详解-Java编程思想

Java中的泛型参考了C++的模板,Java的界限是Java泛型的局限. 2.简单泛型 促成泛型出现最引人注目的一个原因就是为了创造容器类. 首先看一个只能持有单个对象的类,这个类可以明确指定其持有的对象的类型 class Holder1 { private Circle a; public Holder1(Circle a) { this.a = a; } Circle get() { return a; } } 上面的类的可重用性不怎么样,无法持有其他类型的任何对象,下面通过持有Object

java中的泛型(转)

什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样. 可以在集合框架(Collection framework)中看到泛型的动机.例如,Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象. 因为 Map.get(

Java中的泛型 (上) - 基本概念和原理

本节我们主要来介绍泛型的基本概念和原理 后续章节我们会介绍各种容器类,容器类可以说是日常程序开发中天天用到的,没有容器类,难以想象能开发什么真正有用的程序.而容器类是基于泛型的,不理解泛型,我们就难以深刻理解容器类.那,泛型到底是什么呢? 什么是泛型? 一个简单泛型类 我们通过一个简单的例子来说明泛型类的基本概念.实现原理和好处. 基本概念 我们直接来看代码: public class Pair<T> { T first; T second; public Pair(T first, T se

Java基础之Java中的泛型

1.为什么要使用泛型 这里我们俩看一段代码; List list = new ArrayList(); list.add("CSDN_SEU_Cavin"); list.add(100); for (int i = 0; i < list.size(); i++) { String name = (String) list.get(i); //取出Integer时,运行时出现异常 System.out.println("name:" + name); } 本例

Java开发知识之Java中的泛型

Java开发知识之Java中的泛型 一丶简介什么是泛型. 泛型就是指泛指任何数据类型. 就是把数据类型用泛型替代了. 这样是可以的. 二丶Java中的泛型 Java中,所有类的父类都是Object类.所以定义泛型的时候,设计长须的话传入的值与返回的值都是Object类型为主.如果是用具体的实例,就要进行转换了.具体参考向上转型,跟向下转型. JDK 1.5版本才有了泛型机制. 语法如下: class 类名<T >{ public T a; public T b; public void Set

Java中的泛型 --- Java 编程思想

前言 ? 我一直都认为泛型是程序语言设计中一个非常基础,重要的概念,Java 中的泛型到底是怎么样的,为什么会有泛型,泛型怎么发展出来的.通透理解泛型是学好基础里面中非常重要的.于是,我对<Java编程思想>这本书中泛型章节进行了研读.可惜遗憾的是,自己没有太多的经验,有些东西看了几次也是有点懵.只能以后有机会,再进行学习了.但是自己也理解了挺多的.下面就是自己对于泛型的理解与感悟.如有不对,望指出. 概念 由来: Java 一开始设计之初是没有泛型这个特性的,直到jdk 1.5中引入了这个特

夯实Java基础系列13:深入理解Java中的泛型

目录 泛型概述 一个栗子 特性 泛型的使用方式 泛型类 泛型接口 泛型通配符 泛型方法 泛型方法的基本用法 类中的泛型方法 泛型方法与可变参数 静态方法与泛型 泛型方法总结 泛型上下边界 泛型常见面试题 参考文章 微信公众号 Java技术江湖 个人公众号:黄小斜 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下Star.Fork.Watch三连哈,感谢你的

泛型及java中的泛型

当作笔记整理的~~~ 首先,引出堆对象这个概念. 什么是堆对象,就是程序在运行过程中可以随时建立或者删除的对象,可以用new运算符(或malloc函数)或者delete运算符(或free函数).泛型可以看作是一类堆对象. 泛型是程序设计语言的一种特性.允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明. 各种程序设计语言和其编译器.运行环境对泛型的支持均不一样.将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型. 泛型的定义主要有两种:1.在程序编