1、泛型的概念
所谓的泛型就是在类定义时,不为类中的属性和方法指定数据类型,而是在类对象创建时为其指定相应的数据类型。
例如之前使用的List<Student> list = new ArrayList<>();其中<T>就是泛型。
2、为什么使用泛型
例子:要求定义一个Point点类,该类中的属性有x坐标和y坐标。
要求 :x和y的值都为整数类型;x和y的值都为小数类型;x和y的值都为字符串类型;
使用Object定义属性类型
public class Point {
private Object x;
private Object y;
public void show(){
System.out.println("x坐标:"+x+";y坐标:"+y);
}
public Point(Object x, Object y) {
this.x = x;
this.y = y;
}
public Point() {
}
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;
}
}
public class TestPoint {
public static void main(String[] args) {
Point point = new Point(12,23);
point.show();
Point point1 = new Point(23.1,45.1);
point1.show();
Point point2 = new Point("北纬21度","西经70度");
point2.show();
}
}
但是我们会发现一个问题,如果我们传入的两个参数一个为整数,一个为字符串也是可以正常运行,也不会报错,但是与我们的设计要求不符合,就出现了数据类型安全问题,这个问题我们可以使用泛型解决。
将Object类型写成<T>使用泛型定义,这里的T只是一个标识
public class Point<T> {
private T x;
private T y;
public void show(){
System.out.println("x坐标:"+x+";y坐标:"+y);
}
public Point(T x, T y) {
this.x = x;
this.y = y;
}
public Point() {
}
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;
}
}
public class TestPoint {
public static void main(String[] args) {
Point<Integer> point = new Point<>(12,23);
point.show();
Point<Double> point1 = new Point<>(23.1,45.1);
point1.show();
Point<String> point2 = new Point<>("北纬21度","西经70度");
point2.show();
//现在如果两个参数不是相同的类型就会报错,解决了数据类型安全问题
//Point<Double> point3 = new Point<>(21,21.98);
}
}
注意:这里的泛型的类型必须为引用类型,不能为基本类型。
3、定义泛型
泛型可以定义在类上,接口上,方法上。称为泛型类,泛型接口,泛型方法。
泛型解决数据类型的安全性问题的原理是在类声明时通过一个标识表示类中某个属性的数据类型或者是方法的返回值类型或者是参数类型,这样在类声明或者实例化时只需要指定好需要的类型即可。
public class 类名<泛型标志,泛型标志....>{
//类成员
}
(1)定义泛型类
public class Test01 {
public static void main(String[] args) {
Info<Integer,String> info = new Info<Integer,String>();
info.setA(1);
info.setB("我是第二个参数");
System.out.println(info);
}
}
class Info<T,K>{
private T a;
private K b;
public Info() {
}
public Info(T a, K b) {
this.a = a;
this.b = b;
}
public T getA() {
return a;
}
public void setA(T a) {
this.a = a;
}
public K getB() {
return b;
}
public void setB(K b) {
this.b = b;
}
@Override
public String toString() {
return "info{" +
"a=" + a +
", b=" + b +
'}';
}
}
通配符
在开发中对象的引用传递是最常见的,但是如果在泛型类的操作中,在进行引用传递时泛型类型必须匹配才可以传递,否则是无法传递的。如果想传递,可以定义泛型为?通配符。
public class Test04 {
public static void main(String[] args) {
Myself<String> myself = new Myself<>("张三");
fun(myself);
}
//该方法的参数为Myself而且它的泛型为String类型
public static void fun(Myself<?> myself){
myself.show();
}
}
class Myself<T>{
private T name;
public void show(){
System.out.println("name:"+name);
}
public Myself() {
}
public Myself(T name) {
this.name = name;
}
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
@Override
public String toString() {
return "Myself{" +
"name=" + name +
'}';
}
}
受限泛型
在引用传递中,在泛型操作中也可以设置一个泛型对象的范围上限和范围下限。范围上限使用extends关键字声明,表示参数化的类型可能是所指定的类型或者是此类型的子类,而范围下限使用super进行声明,表示参数化的类型可能是所指定的类型或者此类型的父类型。
[设置上限]
声明对象: 类名称<? extends 类> 对象名称;
定义类: [访问权限] 类名称<泛型标识 extends 类>{}[设置下限]
声明对象: 类名称<? super 类> 对象名称;
不能给类设置下限
public class Test02 {
public static void main(String[] args) {
People<Integer> people1 = new People<>(1);
fun1(people1);
//fun2(people1);
People<Number> people2 = new People<>(2);
fun1(people2);
fun2(people2);
People<Object> people3 = new People<>("123");
//fun1(people3);
fun2(people3);
//People2<Object> people4 = new People2<Object>("123");
}
/*people的泛型为number的子类或者本身*/
public static void fun1(People<? extends Number> people){
people.show();
}
/*people的泛型为number的父类或者本身*/
public static void fun2(People<? super Number> people){
people.show();
}
}
class People<T>{
private T a;
public void show(){
System.out.println("a:"+a);
}
public People() {
}
public People(T a) {
this.a = a;
}
public T getA() {
return a;
}
public void setA(T a) {
this.a = a;
}
}
class People2<T extends Number>{
private T a;
public void show(){
System.out.println("a:"+a);
}
public People2() {
}
public People2(T a) {
this.a = a;
}
public T getA() {
return a;
}
public void setA(T a) {
this.a = a;
}
}
(2)泛型接口
在jdk1.5之后泛型也可以定义在接口上了。
public interface 接口名<泛型标志,泛型标志....>{
//静态常量
//抽象方法。
}
public class Test05 {
public static void main(String[] args) {
Mouse<String> mouse = new Mouse<>();
String fun = mouse.fun();
System.out.println("鼠标的方法:"+fun);
Upan upan = new Upan();
String fun1 = upan.fun();
System.out.println("U盘的方法:"+fun1);
}
}
//泛型接口
interface USB<T> {
//接口中定义的常量名全部大写
public static final String NAME="USB";
//抽象方法
T fun();
}
//子类在实现接口时,确定泛型类型
class Upan implements USB<String>{
@Override
public String fun() {
return "我是U盘";
}
}
//子类也实现泛型和父类名相同的泛型
class Mouse<T> implements USB<T>{
@Override
public T fun() {
return null;
}
}
(3)泛型方法
泛型方法的定义与其所在的类是否是泛型类是没有任何关系的,所在的类可以是泛型类,也可以不是泛型类。
[访问权限] ==<泛型标识>== 泛型标识 方法名称(泛型标识 参数名称)
public class Test06 {
public static void main(String[] args) {
String eat = Pet.eat("骨头");
System.out.println(eat);
}
}
class Pet{
//定义泛型方法和该方法所在的类是不是泛型类没有关系
public static <T> T eat(T t){
return t;
}
}