Java泛型详解

一 概念

1.1 为什么需要泛型?

          当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,该对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型。因此,取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现“java.lang.ClassCastException”异常。使用泛型就可以解决此类问题。

1.2 什么是泛型?

       
泛型
(Generic type 或者 generics)是对
Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。         

        可以在集合框架(Collection framework)中看到泛型的动机。例如,Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象。因为 Map.get() 被定义为返回 Object,所以一般必须将 Map.get()
的结果强制类型转换为期望的类型,如下面的代码所示:

Map m = new HashMap();
m.put("key", "blarg");
String s = (String) m.get("key");

要让程序通过编译,必须将 get() 的结果强制类型转换为 String,并且希望结果真的是一个 String。但是有可能某人已经在该映射中保存了不是 String 的东西,这样的话,上面的代码将会抛出 ClassCastException。

理想情况下,您可能会得出这样一个观点,即 m 是一个 Map,它将 String 键映射到 String 值。这可以让您消除代码中的强制类型转换,同时获得一个附加的类型检查层,该检查层可以防止有人将错误类型的键或值保存在集合中。这就是泛型所做的工作。

二 定义和使用

2.1 定义

        泛型参数的命名风格为:推荐你用简练的名字作为形式类型参数的名字(如果可能,单个字符)。最好避免小写字母,这使它和其他的普通的形式参数很容易被区分开来。使用T代表类型,无论何时都没有比这更具体的类型来区分它。这经常见于泛型方法。如果有多个类型参数,我们可能使用字母表中T的临近的字母,比如S。如果一个泛型函数在一个泛型类里面出现,最好避免在方法的类型参数和类的类型参数中使用同样的名字来避免混淆。对内部类也是同样。

例:

<span style="font-size:10px;"> <span style="font-size:12px;">class Notepad<K,V>{       // 此处指定了两个泛型类型
    private K key ;     // 此变量的类型由外部决定
    private V value ;   // 此变量的类型由外部决定
    public K getKey(){
        return this.key ;
    }
    public V getValue(){
        return this.value ;
    }
    public void setKey(K key){
        this.key = key ;
    }
    public void setValue(V value){
        this.value = value ;
    }
};  </span></span>

2.2 使用方法

2.2.1 通配符(?)与上下界(extends ,super )

        在使用泛型类的时候,既可以指定一个具体的类型,如List<String>就声明了具体的类型是String;也可以用通配符?来表示未知类型,如List<?>就声明了List中包含的元素类型是未知的。
通配符所代表的其实是一组类型,但具体的类型是未知的。List<?>所声明的就是所有类型都是可以的。但是List<?>并不等同于List<Object>。List<Object>实际上确定了List中包含的是Object及其子类,在使用的时候都可以通过Object来进行引用。而List<?>则其中所包含的元素类型是不确定。其中可能包含的是String,也可能是
Integer。如果它包含了String的话,往里面添加Integer类型的元素是错误的。正因为类型未知,就不能通过new ArrayList<?>()的方法来创建一个新的ArrayList对象。因为编译器无法知道具体的类型是什么。但是对于List<?>中的元素确总是可以用Object来引用的,因为虽然类型未知,但肯定是Object及其子类。

例:extends通配符,向上造型一个泛型对象的引用

?extends
XX,XX 类是用来限定通配符的上界,XX 类是能匹配的最顶层的类,它只能匹配 XX 类以及 XX 类的子类。

//原始版本
public void drawAll(List<Shape> shapes) {
     for (Shapes:shapes)
		 {
           s.draw(this);
         }
    }
//使用边界通配符的版本
public void drawAll(List<?exends Shape> shapes) {
     for (Shapes:shapes)
		 {
         s.draw(this);
         }
    }

例:?super通配符,向下造型一个泛型对象的引用
? super XX,XX 类是用来限定通配符的下界,XX 类是能匹配的最底层的类,它只能匹配 XX 类及子类。

List<Shape> shapes = new ArrayList<Shape>();
List<? super Cicle> cicleSupers = shapes;
cicleSupers.add(new Cicle()); //OK, subclass of Cicle also OK
cicleSupers.add(new Shape());//Error

2.2.2 泛型接口

 <span style="font-size:12px;">   interface Info<T>{        // 在接口上定义泛型
        public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型
    }
    class InfoImpl<T> implements Info<T>{   // 定义泛型接口的子类
        private T var ;             // 定义属性
        public InfoImpl(T var){     // 通过构造方法设置属性内容
            this.setVar(var) ;
        }
        public void setVar(T var){
            this.var = var ;
        }
        public T getVar(){
            return this.var ;
        }
    };
    public class GenericsDemo24{
        public static void main(String arsg[]){
            Info<String> i = null;        // 声明接口对象
            i = new InfoImpl<String>("汤姆") ;  // 通过子类实例化对象
            System.out.println("内容:" + i.getVar()) ;
        }
    };
    ----------------------------------------------------------
    interface Info<T>{        // 在接口上定义泛型
        public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型
    }
    class InfoImpl implements Info<String>{   // 定义泛型接口的子类
        private String var ;                // 定义属性
        public InfoImpl(String var){        // 通过构造方法设置属性内容
            this.setVar(var) ;
        }
        public void setVar(String var){
            this.var = var ;
        }
        public String getVar(){
            return this.var ;
        }
    };
    public class GenericsDemo25{
        public static void main(String arsg[]){
            Info i = null;      // 声明接口对象
            i = new InfoImpl("汤姆") ;    // 通过子类实例化对象
            System.out.println("内容:" + i.getVar()) ;
        }
    };  </span>

2.2.3 泛型数组

    public class GenericsDemo30{
        public static void main(String args[]){
            Integer i[] = fun1(1,2,3,4,5,6) ;   // 返回泛型数组
            fun2(i) ;
        }
        public static <T> T[] fun1(T...arg){  // 接收可变参数
            return arg ;            // 返回泛型数组
        }
        public static <T> void fun2(T param[]){   // 输出
            System.out.print("接收泛型数组:") ;
            for(T t:param){
                System.out.print(t + "、") ;
            }
        }
    };  

时间: 2024-10-28 15:47:02

Java泛型详解的相关文章

Java泛型详解_java

1. Why --引入泛型机制的原因     假如我们想要实现一个String数组,并且要求它可以动态改变大小,这时我们都会想到用ArrayList来聚合String对象.然而,过了一阵,我们想要实现一个大小可以改变的Date对象数组,这时我们当然希望能够重用之前写过的那个针对String对象的ArrayList实现.     在Java 5之前,ArrayList的实现大致如下: public class ArrayList { public Object get(int i) { ... }

Java泛型详解(上)

一. 什么是泛型 泛型是一种程序设计手段(机制),使用泛型可以让你的代码被很多不同类型的对象所重用,提高代码的重用性,还可以提高代码的可读性和安全性 比如,我们经常使用的ArrayList类,就是一个泛型类,也正因如此,它可以接受很多不同类型的对象 /* 可以根据需要存储不同类型的对象 */ ArrayList<Integer> arraylist = new ArrayList<Integer>(); ArrayList<String> arraylist = new

Java 泛型详解

在日常的开发中,我们会看到别人的框架很多地方会使用到泛型,泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法.泛型的类型参数只能是类类型(包括自定义类),不能是简单类型.本篇博客我们就来详细解析一下泛型的知识. 泛型类定义及使用 使用泛型有什么好处呢?首先我们先看一个例子,假设我们有两个类,代码如下: #StringClass  public class Strin

Java泛型详解(下)

九. 泛型类型的继承规则 假设现在有一个类Employee和它的子类Manager 现在问题来了: Pair<Manager>是Pair<Employee>的子类吗? 答案是:不是 例如,下面的代码将不会编译成功: Manager[] topHonchos = ...; Pair<Employee> result = ArrayAlg.minmax(topHonchos); //Error //minmax方法返回Pair<Manager>, 而不是Pair

Java设计模式详解之门面模式(外观模式)_java

门面模式(Facade Pattern)也叫外观模式,它隐藏系统的复杂性,并向客户端提供一个可以访问系统的接口.这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性,为子系统中的一组接口提供了一个统一的高层访问接口,这个接口使得子系统更容易被访问或使用.这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用. 简而言之,就是把一堆复杂的流程封装成一个接口供给用户更简单的使用,这个设计模式里有三个角色: 1)门面角色( facade ):

Java虚拟机详解----JVM常见问题总结

[正文] 声明:本文只是做一个总结,有关jvm的详细知识可以参考本人之前的系列文章,尤其是那篇:Java虚拟机详解04----GC算法和种类.那篇文章和本文是面试时的重点. 面试必问关键词:JVM垃圾回收.类加载机制.   先把本文的目录画一个思维导图:(图的源文件在本文末尾)   一.Java引用的四种状态: 强引用: 用的最广.我们平时写代码时,new一个Object存放在堆内存,然后用一个引用指向它,这就是强引用. 如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空间不足,Java

java关键字(详解)

基本类型 1 boolean 布尔型 2 byte 字节型 3 char 字符型 4 double 双精度 5 float 浮点 6 int 整型 7 long 长整型 8 short 短整型 9 null 空 10 true 真 11 false 假 程序控制语句 1 break 跳出中断 2 continue 继续 3 return 返回 4 do 运行 5 while 循环 6 if 如果 7 else 否则 8 for 循环 9 instanceof 实例 10 switch 观察 11

Java NIO 详解(一)

NIO即新的输入输出,这个库是在JDK1.4中才引入的.它在标准java代码中提供了高速的面向块的IO操作. 一.基本概念描述 1.1 I/O简介 I/O即输入输出,是计算机与外界世界的一个借口.IO操作的实际主题是操作系统.在java编程中,一般使用流的方式来处理IO,所有的IO都被视作是单个字节的移动,通过stream对象一次移动一个字节.流IO负责把对象转换为字节,然后再转换为对象. 关于Java IO相关知识请参考我的另一篇文章:Java IO 详解 1.2 什么是NIO NIO即New

HBase Java API详解

[本文转自HBase Java API详解] HBase是Hadoop的数据库,能够对大数据提供随机.实时读写访问.他是开源的,分布式的,多版本的,面向列的,存储模型. 在讲解的时候我首先给大家讲解一下HBase的整体结构,如下图: HBase Master是服务器负责管理所有的HRegion服务器,HBase Master并不存储HBase服务器的任何数据,HBase逻辑上的表可能会划分为多个HRegion,然后存储在HRegion Server群中,HBase Master Server中存