Java面向对象编程-泛型

泛型

1、泛型问题的引出

泛型从JDK1.5之后追加到Java语言里面的,其主要的是为了解决ClassCastException的问题,在进行对象的向下转型时永远都存在有安全隐患,而Java希望通过泛型慢慢解决掉此类问题

泛型问题的引出
现在假设说定义一个描述x与y坐标的处理类,并且在这个类之中允许开发者保存有三类数据

  • 整型数据:x=10、y=20

  • 浮点型数据:x=10.1、y=20.9

  • 字符串型数据:x=东经120度、北纬30度

于是在设计Point类的时候就需要去考虑具体的x和y属性的类型,这个类型要求保存以上三种数据,很明显,最普通的做法就是利用Object类来进行定义,因为存在有以下转型关系:

  • 整型数据:基本数据类型 →包装为Integer类对象 → 自动向上转型为Object类
  • 浮点型数据:基本数据类型 →包装为Integer类对象 → 自动向上转型为Object类
  • 字符型数据:String类型 → 自动向上转型为Object类
    定义Point类:
class Point{
	private Object x ;
	private Object y ;
	public void setX(Object x){
		this.x = x;
	}
	public void setY(Object y){
		this.y = y ;
	}
	public Object getX(){
		return x;
	}
	public Object getY(){
		return y;
	}
}

下面进行内容的设置
进行正确的内容操作:

public class JavaDemo {
	public static void main(String[] args) {
		//第一步:根据需求进行内容的设置
		Point point = new Point();
		point.setX(10);	//自动装箱
		point.setY(20);	//自动装箱
		//第二步:从里面获取数据
		int x = (Integer)point.getX();
		int y = (Integer)point.getY();
		System.out.println("X坐标:" + x + "\nY坐标:" + y);
	}
}

本程序之所以可以解决当前的设计问题,主要的原因在于,Object可以接受所有的数据类型,但正因为如此,所以本代码也会出现严重的安全隐患

public class JavaDemo {
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		Point point = new Point();
		point.setX(10);
		point.setY(北纬20);	//这里进行了更改
		int x = (Integer)point.getX();
		int y = (Integer)point.getY();
		System.out.println("X坐标:" + x + "\nY坐标:" + y);
	}
}

此时的程序明显出现了问题,如果在程序编译的时候实际上是不会有任何的错误产生的,而程序执行的时候就会出现ClassCastException异常类型,所以本程序的设计是存在有安全隐患的。
而这个安全隐患存在的一局在于使用了Object类型,因为Object可以涵盖的范围太广了,而对于这样的错误如果可以直接出现在编译的过程之中,那么就可以避免运行时的尴尬

2、泛型的基本定义

如果想要避免项目之中出现ClassCastException最好的做法是可以直接回避掉对象的强制转换,
所以在JDK1.5之后,提供有泛型技术,而泛型的本质在于,
类中的属性或方法的参数与返回值的类型可以由对象实例化的时候动态决定

那么此时就需要在类定义的时候明确的定义占位符(泛型标记)

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 Object getX(){
		return x;
	}
	public Object getY(){
		return y;
	}
}

此时Point类中的x和y属性的数据类型并不确定,而是由外部来决定

提示:关于默认的泛型类型

  • 由于泛型是属于JDK1.5之后的产物,但是在这之前已经有不少内容的程序类或者是接口广泛的应用到项目的开发之中,

但是有了泛型之后,原始的程序类依然可以使用,所以如果不设置泛型类型时,自动将使用Object作为类型,以保证程序的正常执行,但是在编译的过程之中会出现警告信息

泛型定义完成后可以在实例化对象的时候进行泛型类型的设置,一旦设置之后,里面的x与y的类型就直接与当前的对象绑定了

 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 this.x;
	}
	public T getY(){
		return this.y;
	}
}
public class JavaDemo {
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		Point<Integer> point = new Point<Integer>();
		point.setX(10);
		point.setY(20);
		int x = point.getX();
		int y = point.getY();
		System.out.println("X坐标:" + x + "\nY坐标:" + y);
	}
}

现在的程序代码之中,由于Point类里面设置的泛型类型为Integer,这样所有的对应此泛型的属性、变量、方法返回值就将全部替换为Integer(只局限于此对象之中),这样在进行处理的时候如果发现设置的内容有错误,则会在程序编译的时候错误提示,同时也避免了对象的向下转型处理(可以避免安全隐患)

泛型的使用注意点:

  • 泛型之中只允许设置引用类型,如果现在要操作基本类型必须使用包装类
  • 从JDK1.7之后,泛型对象实例化可以简化为 Point point = new Point<>();
    使用泛型可以解决大部分类对象的强制转换处理,这样的程序才是一个合理的设计

3、泛型方法

在之前的程序类里面实际上已经可以发现在泛型类之中如果将泛型标记写在了方法上,那么这样的方法就被称为泛型方法,但是需要注意的是,泛型方法不一定要出现在泛型类之中

public class JavaDemo {
	public static void main(String[] args) {
		Integer num [] = fun(1,2,3);
		for(int temp : num){
			System.out.print(temp + "、");
		}
	}
	public static <T> T [] fun(T ... args){
		return args ;
	}
}

在后期进行项目开发的时候,这种泛型方法很常见,与之前的工厂设计为例
如果此时一个项目有上千个接口,到时候满眼望去都是绝望的身影
利用泛型改进工厂设计模式:

4、泛型接口

泛型除了可以在类上定义之外也可以直接在接口之中进行使用
定义一个泛型接口:

interface IMessage<T>{
	public String echo(T t);
}
class MessageImpl<S> implements IMessage<S>{
	public String echo (S t){
		return "【ECHO:】" + t ;
	}
}
public class JavaDemo {
	public static void main(String[] args) {
		IMessage <String> msg = new MessageImpl<>();
		System.out.println(msg.echo("www.shenmenglin.xyz")) ;
	}
}

实现方式二:在子类是父类接口的时候直接定义出具体泛型类型

interface IMessage<T>{
	public String echo(T t);
}
class MessageImpl implements IMessage<String>{
	public String echo (String t){
		return "【ECHO:】" + t ;
	}
}
public class JavaDemo {
	public static void main(String[] args) {
		IMessage <String> msg = new MessageImpl();
		System.out.println(msg.echo("www.shenmenglin.xyz")) ;
	}
}

如果从概念和实现上来讲并不复杂,但是在日后会遇见大量出现有泛型的接口,这个时候一定要清楚两种实现原则

5、泛型通配符

虽然泛型帮助开发者解决了一系列的对象的强制转换所带来的的安全隐患,但是从另一方面讲,泛型也带来了新的问题:引用传递处理

class Message<Type>{
	private Type content;
	public Message(){}
	public Message(Type content){
		this.content = content;
	}
	public void setContent(Type content){
		this.content = content;
	}
	public Type getContent(){
		return content;
	}
}
public class JavaDemo {
	public static void main(String[] args) {
		Message<String> msg = new Message<>();
		msg.setContent("www.shenmenglin");
		fun(msg);
	}
	public static void fun(Message<String> temp){
		System.out.println(temp.getContent());
	}

}

这个时候问题出现了,而问题的关键就在于fun()方法上,如果真的使用泛型,不可能知识一种类型,也就是说fun()方法应该可以接收任意种泛型类型的Message对象。
但是这个时候它只能接收Message类型,因此需要一种可以接收所有的泛型类型,并且不能够谢盖里面的数据(允许获取),那么就需要通过通配符?来解决这个问题
使用通配符:

public class JavaDemo {
	public static void main(String[] args) {
		Message<String> msgA = new Message<>();
		Message<Integer> msgB = new Message<>();
		msgA.setContent("www.shenmenglin");
		msgB.setContent(123235);
		fun(msgA);
		fun(msgB);
	}
	public static void fun(Message<?> temp){
		System.out.println(temp.getContent());
	}
}

此时在fun()方法里面由于采用了Message结合通配符的处理所以可以接受所有类型,并且可以获取数据
在?这个通配符的基础上实际上还提供有两个类小的通配符:

  • ?extends 类:设置泛型上限
  • 例如:定义 ?extends Number :表示该泛型类型只允许设置Number 或者Number 的子类
  • ?super 类:设置泛型下限
    • 例如:定义 ?super String :只能够使用String或者String父类
      泛型的上限配置:
class Message<Type extends Number>{
	private Type content;
	public Message(){}
	public Message(Type content){
		this.content = content;
	}
	public void setContent(Type content){
		this.content = content;
	}
	public Type getContent(){
		return content;
	}
}
public class JavaDemo {
	public static void main(String[] args) {
		Message<String> msgA = new Message<>();
		msgB.setContent(1234);
		fun(msgA);
	}
	public static void fun(Message<? extends Number> temp){
		System.out.println(temp.getContent());
	}

}

泛型的下限配置:

class Message<Type >{
	private Type content;
	public Message(){}
	public Message(Type content){
		this.content = content;
	}
	public void setContent(Type content){
		this.content = content;
	}
	public Type getContent(){
		return content;
	}
}
public class JavaDemo {
	public static void main(String[] args) {
		Message<String> msgB = new Message<>();
		msgB.setContent("www.shenmenglin.xyz");
		fun(msgB);
	}
	public static void fun(Message<? super String> temp){
		System.out.println(temp.getContent());
	}
}

对于通配符而言是一个重要的概念,并且要求一定要理解此概念的定义,在日后学习Java一些系统类库的时候会见到大量的通配符使用

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MyRedScarf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值