Java之泛型学习笔记
1. 泛型技术的产生背景
1.1 泛型的引出
保存多种数据类型的一堆数据时,我们会考虑Object类型:
- int自动装箱为Interger,Interger向上转型为Object
- double自动装箱为Double,Double向上转型为Object
- String向上转型为Object
注意:向上转型的核心目的在统一操作的参数上,而向下转型的目的是操作子类定义的特殊功能。向下转型是一件非常不安全的操作。
从JDK1.5后增加了泛型技术,而泛型技术的核心意义在于:类在定义的时候,可以使用一个标记,此标记表示表示类中属性或方法参数的类型标记,在使用的时候才动态的设置。
1.2 泛型操作的实现
使用泛型示例:
import java.awt.*;
/*
这是关于泛型的一个Demo。
*/
class Point<T>{
private T x;
private T y;
public void setX(T x) {
this.x = x;
}
public void setY(T y) {
this.y = y;
}
public T getX() {
return x;
}
public T getY() {
return y;
}
}
public class Demo {
public static void main(String[] args) {
//第一步:设置数据
Point<String> p=new Point<String>();
//利用的就是包装类的自动装箱功能
p.setX("我是");
p.setY("杨婷婷");
System.out.println(p.getX()+p.getY());
}
}
发现使用泛型以后,所有类中属性的类型都是动态设置的,而所有使用泛型标记方法参数类型也发生变化,这就避免了向下转型的问题,从而解决了类转换的安全隐患。
需要特别注意的是,要想使用泛型,那么它能够采用的类型只能是类,即:不能是基本类型,只能是引用类型(String、Double、Integer、List等)。
对于泛型有两点说明:
- 如果在使用泛型类或者接口时,没有设置泛型的具体类型,那么会出现编译时的警告,将使用Object表示泛型。
- 从JDK1.7开始可以简化声明泛型。即允许这样定义:
Point<Integer> p=new Point<>();
2. 通配符的使用
2.1 背景
注意:泛型不能作为方法重载的参数。即下列代码不构成重载。
解决参数传递问题:
import java.awt.*;
/*
这是关于泛型的一个Demo。
*/
class Point<T>{
private T x;
private T y;
public void setX(T x) {
this.x = x;
}
public void setY(T y) {
this.y = y;
}
public T getX() {
return x;
}
public T getY() {
return y;
}
}
public class Demo {
public static void main(String[] args) {
//第一步:设置数据
Point<Integer> p=new Point<>();
Point<String> p1=new Point<>();
//利用的就是包装类的自动装箱功能
p.setX(12);
p.setY(18);
p1.setX("ytt");
p1.setY("hello");
fun(p);
fun(p1);
}
public static void fun(Point p){ //此处不设置泛型,会出现警告
System.out.println(p.getX()+" "+p.getY());
}
}
但仍然存在警告。因为只要不设置泛型,就一定存在警告信息。所以现在需要解决的是,需要有一种方式可以接收一个类的任意的泛型,但是不可以修改,只能取出,那么就可以使用“?”来描述。即:
import java.awt.*;
/*
这是关于泛型的一个Demo。
*/
class Point<T>{
private T x;
private T y;
public void setX(T x) {
this.x = x;
}
public void setY(T y) {
this.y = y;
}
public T getX() {
return x;
}
public T getY() {
return y;
}
}
public class Demo {
public static void main(String[] args) {
//第一步:设置数据
Point<Integer> p=new Point<>();
Point<String> p1=new Point<>();
//利用的就是包装类的自动装箱功能
p.setX(12);
p.setY(18);
p1.setX("ytt");
p1.setY("hello");
fun(p);
fun(p1);
}
public static void fun(Point<?> p){
System.out.println(p.getX()+" "+p.getY());
}
}
2.2 "?"通配符
- 在"?"通配符基础上还会有两个子的通配符:
- ?extends 类:设置泛型上限,可以在声明上和方法参数上使用。
? extends Number
上述代码意味着可以设置Number类或其子类 (Integer、Double)。
import java.awt.*;
/*
这是关于泛型的一个Demo。
*/
class Point<T extends Number>{ //限定只能使用Number类或其子类
private T x;
private T y;
public void setX(T x) {
this.x = x;
}
public void setY(T y) {
this.y = y;
}
public T getX() {
return x;
}
public T getY() {
return y;
}
}
public class Demo {
public static void main(String[] args) {
//第一步:设置数据
Point<Integer> p=new Point<>(); //若设置String类会报错
Point<Double> p1=new Point<>();
//利用的就是包装类的自动装箱功能
p.setX(12);
p.setY(18);
p1.setX(11.0);
p1.setY(28.2);
fun(p);
fun(p1);
}
public static void fun(Point<? extends Number> p){
System.out.println(p.getX()+" "+p.getY());
}
}
- ?super 类:设置泛型下限,方法参数上使用。
?super String
上述代码意味着只能够设置String或者它的父类Object。
import java.awt.*;
/*
这是关于泛型的一个Demo。
*/
class Point<T>{ //此处不能写 Point<T ? super Double>
private T x;
private T y;
public void setX(T x) {
this.x = x;
}
public void setY(T y) {
this.y = y;
}
public T getX() {
return x;
}
public T getY() {
return y;
}
}
public class Demo {
public static void main(String[] args) {
//第一步:设置数据
Point<Number> p=new Point<>(); //若设置String类会报错
Point<Double> p1=new Point<>();
//利用的就是包装类的自动装箱功能
p.setX(12);
p.setY(18);
p1.setX(11.0);
p1.setY(28.2);
fun(p);
fun(p1);
}
public static void fun(Point<? super Double> p){
System.out.println(p.getX()+" "+p.getY());
}
}
3. 泛型接口
3.1 概念
在之前都是将泛型定义在一个类里面,那么泛型也可以在接口上声明,这样的接口称为泛型接口。
3.2 实现
- 定义泛型接口
public interface IPoint<T>{
public void print(T t);
}
- 在接口上必须要定义其相应的子类,定义子类有两种形式。
- 在子类继续设置泛型
public class Demo<T> implements IPoint<T> {
public void print(T t) {
System.out.println(t);
}
public static void main(String[] args) {
IPoint<String> iPoint=new Demo<String>();
iPoint.print("Hello");
}
}
- 在子类不设置泛型,为父接口明确定义泛型类型。
public class Demo implements IPoint<String> {
public void print(String t) {
System.out.println(t);
}
public static void main(String[] args) {
IPoint<String> iPoint=new Demo();
iPoint.print("Hello");
}
}
以上两类代码应该清楚其继承流程。
4. 泛型方法
泛型方法不一定非要定义在支持泛型的类里面。
泛型方法定义:
public class Demo{
public static void main(String[] args) {
String str=fun("HELLO");
System.out.println(str.length());
}
//T的类型由传入的参数决定
public static <T> T fun(T t) {
return t;
}
}
5. 总结
- 泛型解决的是向下转型所带来的安全隐患,其核心在于在声明类或接口时不设置参数或属性的类型;
- “?”通配符可以接收任意的泛型类型,但是只能够取出不能够修改。