浏览以下内容前,请点击并阅读 声明

 泛型的使用能使类型名称作为类或者接口定义中的参数,就像一般的参数一样,使得定义的类型通用性更强。

 泛型的优势:

  • 编译具有严格的类型检查

  java编译器对于泛型代码的类型检查更加严格,能够发现普通代码中的一些运行时错误。

  • 消除类型转化  
//如下代码未使用泛型,需要进行类型的转化
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); //泛型的使用可以不适用类型转化
List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0); // no cast
  • 能够使程序员实现通用的算法

  通过使用泛型,使得一类不同的类型能够进行通用的运算。

1 泛型

   泛型是将类型参数化的类或者接口。

1.1 泛型的声明

  一般泛型的声明类似如下:

class name<T1, T2, ..., Tn> { /* ... */ }

  尖括号中的参数就是类型参数,参数由逗号隔开,类型参数可以是任何非基本数据类型的任何类型,泛型的接口声明与上述泛型类类似。

1.2 泛型参数命名

  一般情况下,泛型类型参数的名称是单个大写字母,和变量名称鲜明地区分开来。

  最通用的参数类型参数名称为:

  • E - 元素 (被java集合框架应用)
  • K - 键
  • N - 数字
  • T - 类型
  • V - 值
  • S,U,V 等等 - 第二,第三, 第四类型

  在javaSE API中这些名称被广泛使用

1.3 调用和实例化泛型

  如需引用一个泛型,首先要进行一个泛型的调用,如下所示:

//传入类型参数,如下为String作为类型参数
ArrayList<String> list;

  上述代码可以看做个调用方法类似,不过是以类型为参数,这一过程叫参数化类型,实例化泛型的语法如下:

//一下声明和实例化一步完成
ArrayList<String> list=new ArrayList<String>();

1.4 钻石

  java7以后,只要编译器能够根据代码的上下文判断类型参数,就可以将泛型的构造器的类型实参留空(<>)由于空的尖括号形状就像钻石,所以非正式的成为钻石,如上述代码可以简写为:

//注意构造器内的参数已经省略
ArrayList<String> list=new ArrayList<>();

1.5 参数化类型作为类型参数

  泛型的类型参数也可以是参数话的泛型,如:

//省略构造器类型参数
ArrayList<List<String>> list=new ArrayList<>();

2 原始类型

2.1定义

  原始类型是指没有类型参数的泛型。例如一下声明一个原始类型:

//ArrayList是一个泛型,因此List变量是原始类型
ArrayList list=new ArrayList();

  原始类型的旧的java版本的遗产,因为许多API类如集合类在JDK5之前不是泛型,为了向下兼容,将一个参数化的泛型对象赋值给一个原始类型是允许的:

Box<String> stringBox = new Box<>();
Box rawBox = stringBox; // OK

  不过反过来,将一个原始类型赋值给一个参数化的泛型,编译器将会给出警告:

Box rawBox = new Box();           // rawBox是 Box<T>的原始类型
Box<Integer> intBox = rawBox; // 警告: unchecked conversion

  如果使用原始类型去调用队形的泛型的泛型方法,同样也会得到警告:

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8); // warning: unchecked invocation to set(T)

  编译器警告表明原始类型绕过了类型的检查,而将可能出错的风险留到了运行时,所以尽量不要使用原始类型。

2.2 未检查错误信息

  如之前所提到的,当泛型和传统语法混用时,你将会遇到一些如下的警告消息:

    Note: Example.java uses unchecked or unsafe operations.   

    Note: Recompile with -Xlint:unchecked for details.

  “unchecked”(未检查的)这一术语表明编译器没有足够的关于类型信息来执行检查以确保类型的安全,编译器默认警用未检查警告,但是会给出提示,如果想要启用未检查警告,在编译时加入参数 -Xlint:unchecked

  如果想要完全禁用未检查警告,可以编译时加入参数-Xlint:-unchecked(注意与上述参数区别)或者使用注释@SuppressWarnings("unchecked")

3 泛型方法

  泛型方法是指引入自身的参数类型的方法,就像泛型的类型参数一样,不过方法的类型参数的使用范围仅限于方法自身。可以定义静态和非静态的泛型方法,同时也可以定义使用泛型构造器。

  泛型方法的语法含有位于方括号内的类型参数,位于方法返回类型之前,当然,非泛型的类也可以包含泛型方法。

  如下举例说明泛型方法的声明:

public class Util {
//以下方法为泛型方法
public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
} public class Pair<K, V> { private K key;
private V value; public Pair(K key, V value) {
this.key = key;
this.value = value;
} public void setKey(K key) { this.key = key; }
public void setValue(V value) { this.value = value; }
public K getKey() { return key; }
public V getValue() { return value; }
}

完整的调用泛型方法的语法为:

//以下语句利用了上述定义的类
Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);

当然,如果编译其能够推测出类型,可省略泛型方法的类型参数:

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.compare(p1, p2);

4 限制类型参数范围

  有时候你可能需要限定泛型的类型参数,比如限定某个泛型的类型参数只能为Number或者是其子类或者继承类。

  声明类型参数的限定,就是在参数名称之后跟上extends关键词,然后跟上其上限,如Number,在这里extends通常意义上是指类的extends和接口的implements。,如一下方法的声明:

public <U extends Number> void inspect(U u){
System.out.println("T: " + t.getClass().getName());
System.out.println("U: " + u.getClass().getName());
}

  一个类型参数可以有多重限定,即extends关键词后跟多个上限,用符号&隔开:

class D <T extends A & B & C> { /* ... */ }

5 泛型,继承和子类型

  java中可以将一个类型的对象赋值给另外一个类型的变量,如果两个类型相兼容的话,如可以将Integer类型的对象赋值给类型为Object的变量。在面向对象的术语中,这是一种叫做“是一个”的关系,如Integer类型是一个Object类型,因此允许上述的赋值。方法包括泛型的方法的参数的传递也是如此,如

//如下进行类型参数化的ArrayList类型的元素类型为Number
ArrayList<Number> list=new ArrayLIst<Number>();
//其add方法参数类型也是Number,也可以使用其子类Integer的实例
list.add(new Integer(10));

  但是对于泛型的子类型关系,与普通的类型是有区别的,如下图所示:

箭头表示子类型的关系,如Integer是Number的子类型,而Box<Integer>则不是Box<Number>的子类型,Box<Integer>和Box<Number>的共同的父类型是Object。

  你可以通过继承或者实现一个泛型来成为该泛型一个子类型。类或者接口的类型参数之间的关系是由extends和implements语句决定的。

  比如集合类,ArrayList<E>实现List<E>,List<E>继承Collection<E>所以ArrayList<String>是List<String>的子类型,List<String>是Collection<String>的子类型,只要不变更类型参数,这种继承关系就会保留:

现在自定义一个新的继承List接口的接口:

//注意类型参数的名称
interface PayloadList<E,P> extends List<E> {
void setPayload(int index, P val);
...
}

  类型参数化的类型则由如下关系:

java基础-泛型1的更多相关文章

  1. 一天一个Java基础——泛型

    这学期的新课——设计模式,由我仰慕已久的老师传授,可惜思维过快,第一节就被老师挑中上去敲代码,自此在心里烙下了阴影,都是Java基础欠下的债 这学期的新课——算法设计与分析,虽老师不爱与同学互动式的讲 ...

  2. Java 基础 -- 泛型、集合、IO、反射

    package com.java.map.test; import java.util.ArrayList; import java.util.Collection; import java.util ...

  3. java基础-泛型举例详解

    泛型 泛型是JDK5.0增加的新特性,泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数.这种类型参数可以在类.接口.和方法的创建中,分别被称为泛型类.泛型接口.泛型方法. 一.认识泛型 在没 ...

  4. Java基础 - 泛型详解

    2022-03-24 09:55:06 @GhostFace 泛型 什么是泛型? 来自博客 Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了&quo ...

  5. java基础-泛型3

    浏览以下内容前,请点击并阅读 声明 8 类型擦除 为实现泛型,java编译器进行如下操作进行类型擦除: 如果类型参数有限制则替换为限制的类型,如果没有则替换为Object类,变成普通的类,接口和方法. ...

  6. java基础 泛型

    泛型的存在,是为了使用不确定的类型. 为什么有泛型? 1. 为了提高安全 2. 提高代码的重用率 (自动 装箱,拆箱功能) 一切好处看代码: package test1; import java.la ...

  7. java基础-泛型2

    浏览以下内容前,请点击并阅读 声明 6 类型推测 java编译器能够检查所有的方法调用和对应的声明来决定类型的实参,即类型推测,类型的推测算法推测满足所有参数的最具体类型,如下例所示: //泛型方法的 ...

  8. Java基础---泛型、集合框架工具类:collections和Arrays

    第一讲     泛型(Generic) 一.概述 1.JDK1.5版本以后出现的新特性.用于解决安全问题,是一个类型安全机制. 2.JDK1.5的集合类希望在定义集合时,明确表明你要向集合中装入那种类 ...

  9. Java基础——泛型

    一.定义 泛型(generic)是指参数化类型的能力.可以定义带泛型类型的类或方法,随后编译器会用具体的类型来替换它(泛型实例化).使用泛型的主要优点是能够在编译时,而不是在运行时检测出错误.它是jd ...

随机推荐

  1. PHP文件大小格式化函数合集

    比如碰到一个很大的文件有49957289167B,大家一看这么一长串的数字后面单位是字节B,还是不知道这个文件的大小是一个什么概念,我们把它转换成GB为单位,就是46.53GB.用下面这些函数就可以完 ...

  2. JAVA语言规范-线程和锁章节之同步、等待和通知

    JAVA语言规范:线程和锁 1 同步 java编程语言提供了线程间通信的多种机制.这些方法中最基本的是同步化,此方法是使用监视器实现的.JAVA中每个对象与一个监视器相关联,一个线程可以加锁和解锁监视 ...

  3. ARCGIS常用几种本地数据AE初始化

    1.Personal GDB 新建一个在E盘的名为test的mdb: IWorkspaceFactory workspaceFactory = new AccessWorkspaceFactoryCl ...

  4. wait、notify、sleep、interrupt对比分析

    对比分析Java中的各个线程相关的wait().notify().sleep().interrupt()方法 方法简述 Thread类 sleep:暂停当前正在执行的线程:(类方法) yield:暂停 ...

  5. java 获取文件列表,并按照文件名称排序

    需求:获取全部的日志文件,并按照文件名称倒序排列,把最新的文件放在最前1.获取全部的日志文件:(方法:public List<String> ergodic(File file,List& ...

  6. [Head First设计模式]生活中学设计模式——状态模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...

  7. PHP变量入门教程(1)基础

    基础 PHP 中一个美元符号后面跟上一个变量名称,即表示一个变量.变量的名称是对大小写敏感的. 变量名与 PHP 中其它的标签一样遵循相同的规则.一个有效的变量名由字母或者下划线开头,后面跟上任意数量 ...

  8. 2.2WebApi路由在Action上

    这篇文章描述 ASP.NET Web API 如何将 HTTP 请求路由到特定的操作在控制器上. 有关路由的高级别概述,请参见ASP.NET Web API 的路由. 本文着眼于路由进程的详细信息.如 ...

  9. Codeforces 696 D. Legen...

    Description 每个字符串有些价值,问生成长度为 \(l\) 的字符串最多能获得多少价值,总字符数不超过 \(200\), \(l\leqslant 10^{14}\) . Sol AC自动机 ...

  10. 提取刷机包内system.new.dat文件

    转换 使用python脚本sdat2img来完成 sdat2img.py system.transfer.list system.new.dat system.img 输出信息 Skipping co ...