泛型问题引出
泛型主要目的是为了解决ClassCastException问题,在对象进行向下转型时存在安全隐患,而Java希望通过泛型解决安全性问题。
假设要定义一个描述x与y坐标的处理类,并且在这个类中允许开发者保存三类数据:
·整型数据:x=10、y=20;
·浮点数据:x=10.2、y=20.9;
·字符串型:x=“兰州”、y=“理工”。
于是在设计类的时候要具体考虑x与y的数据类型,最原始的方法是通过Object类进行定义,因为存在如下转型关系:
·整型数据:基本数据类型——包装为Integer类对象——自动向上转型为Object类;
·浮点型数据:基本数据类型——包装为Double类对象——自动向上转型为Object类;
·字符型数据:String类对象——自动向上转型为object;
例子:定义如下
class Point{
private Object x;
private Object y;
public Point (Object x,Object y){
this.x=x;
this.y=y;
}
public void setX(Object x){
this.x=x;
}
public void setY(Object y){
this.y=y;
}
public Object getX(){
return this.x;
}
public Object getY(){
return this.y;
}
}
public class Jiekou{
public static void main (String [] args){
Point a=new Point(10,20);
Point b=new Point(10.8,20.6);
//1.内容设置
//a.setX(10);//自动装箱
//a.setY(20);//自动装箱
//2.获取数据
int x=(Integer) a.getX();
int y=(Integer) a.getY();
System.out.println(x+","+y);//10,20
double m=(Double) b.getX();
double n=(Double) b.getY();
System.out.println(m+","+n);//10.8,20.6
}
}
以上方法存在严重安全隐患,编译时错误无法显示。安全隐患存在依据在于使用Object过程中,因为其覆盖范围太广。Object是泛型产生主要依据。
例子:观察一个错误代码
class Point{
private Object x;
private Object y;
public Point (Object x,Object y){
this.x=x;
this.y=y;
}
public Object getX(){
return this.x;
}
public Object getY(){
return this.y;
}
}
public class Jiekou{
public static void main (String [] args){
//1.内容设置
Point a=new Point(10,"同时");
Point b=new Point(10.8,20.6);
//2.获取数据
int x=(Integer) a.getX();
int y=(Integer) a.getY();
System.out.println(x+","+y);
double m=(Double) b.getX();
double n=(Double) b.getY();
System.out.println(m+","+n);
}
}
运行:
Exception in thread “main” java.lang.ClassCastException: java.base/java.lang.String cannot be cast to java.base/java.lang.Integer
at fanxing.Fanxing.main(Fanxing.java:23)
泛型基本定义
如果想要避免java.lang.ClassCastException,最好做法是可以直接回避对象强制转换。泛型的本质在于类中属性或方法的参数与返回值可以由对象实例化的时候动态决定。那么此时需要类定义时候明确定义占位符标记。
此时point类中的x与y属性并不确定,而是由外部条件决定。
泛型定义完成之后类型可以在泛型实例化进行设定。
例子:泛型的基本定义与使用
class Point <T>{
private T x;
private T y;
public Point (T x,T y){
this.x=x;
this.y=y;
}
public T getX(){
return this.x;
}
public T getY(){
return this.y;
}
}
public class Jiekou{
public static void main (String [] args){
//1.内容设置
Point <Integer>a=new Point<Integer>(10,10);
//Point b=new Point(10.8,20.6);
//2.获取数据
int x=a.getX();
int y=a.getY();
System.out.println(x+","+y);//10,10
}
}
由于在代码中point设置泛型为Integer,所以所有对应此泛型的所有方法、属性的返回值全部为Integer,但是只局限于此对象。 此时如果内容有错误会进行内容提示,也避免了向下转型处理。
泛型使用注意:
·泛型只允许使用于基本引用类型,如果要操作基本类型必须使用包装类;
·错误示范:Point a=new Point(10,10);*
·泛型对象实例化可以简化为:Point a=new Point<>(10,10);,其后面可以省略。
泛型通配符
泛型在解决对象强制转换带来的安全隐患问题的同时,也带来了一些列问题:引用传递处理。
例子:问题产生
class Point <T>{
private T x;
public Point (T x){
this.x=x;
}
public T getX(){
return this.x;
}
}
public class Jiekou{
public static void main (String [] args){
Point <String> a=new Point < > ("woshixuesheng");
fun(a);
}
public static void fun(Point<String> b){
System.out.println(b.getX());//woshixuesheng
}
}
如果此时存在其它类型数据,则编译时会发生错误。
这个时候问题也就出现了,问题的关键在于fun()方法,如果真正情况下使用泛型,不可能只有一种类型,所以fun()方法应该可以接受所有类型。实际上此时fun()方法只能接受一种(String)数据类型。
例子:不设置泛型
class Point <T>{
private T x;
public Point (T x){
this.x=x;
}
public void setX(T x){
this.x=x;
}
public T getX(){
return this.x;
}
}
public class Jiekou{
public static void main (String [] args){
//1.内容设置
Point <String> a=new Point < > ("woshixuesheng");
fun(a);
Point <Double> b=new Point < > (56.6);
fun(b);
}
public static void fun(Point A){
A.setX(3.2);//修改方法
System.out.println(A.getX());
}
}
输出结果:
3.2
3.2
此时没有设置泛型,可以接收所有的泛型数据,但是发现方法可以对数据进行修改。此时需要通过通配符“?” 来解释避免数据修改以及接受各种数据类型。此时需要可以接收所有的泛型数据类型并且不允许数据修改,则可以使用通配符。
例子:使用通配符
class Point <T>{
private T x;
public Point (T x){
this.x=x;
}
public void setX(T x){
this.x=x;
}
public T getX(){
return this.x;
}
}
public class Jiekou{
public static void main (String [] args){
Point <String> a=new Point <> ("woshixuesheng");
fun(a);
fun(b);
}
public static void fun(Point<?> A){
// A.setX(3.0);
System.out.println(A.getX());
}
}
此时在fun()方法中采用了通配符处理,可以接受所有数据类型,并且不允许修改获取数据,若发生修改,则编译时产生错误错误: 不兼容的类型: double无法转换为CAP#1。
在“?”基础之上还有两类通配符:
·?extends 类:设置泛型上限;
例如:定义“?extends Number”:表示该泛型只允许设置Number及其子类。
·?super类:设置泛型下限;
例如:定义“?super String”:只允许使用String及其父类。
例子:观察泛型上限配置
class Point <T extends Number>{ //此处需要设置上限
private T x;
public Point (T x){
this.x=x;
}
public void setX(T x){
this.x=x;
}
public T getX(){
return this.x;
}
}
public class Jiekou{
public static void main (String [] args){
Point <Double> b=new Point <> (56.6);
fun(b);
}
public static void fun(Point <? extends Number>A){
System.out.println(A.getX());
}
}
例子:设置泛型下限
class Point < T >{ //此处不需设置下限
private T x;
public Point (T x){
this.x=x;
}
public void setX(T x){
this.x=x;
}
public T getX(){
return this.x;
}
}
public class Jiekou{
public static void main (String [] args){
Point <String> a=new Point <> ("woshixuesheng");
fun(a);
}
public static void fun(Point <? super String >A){
System.out.println(A.getX());
}
}
泛型接口
泛型除了在类中定义外,也可以在接口中定义泛型。
例子:接口中定义泛型
interface IMessage<T>{
public abstract String fun(T t);
}
对于泛型接口子类而言,有两种实现方式:
1·在子类中继续设置泛型定义,对象实例化时候实现泛型;
例子:
interface IMessage<T>{
public abstract String get(T t);
}
class A <S> implements IMessage <S>{
public String get(S t){
return "傻蛋蛋"+t;
}
}
public class Jiekou{
public static void main (String [] args){
IMessage <String> a=new A < >();
System.out.println(a.get("guawawa"));//傻蛋蛋guawawa
}
}
2·在子类实现父接口时候直接定义出数据类型
例子:
interface IMessage<T>{
public abstract String get(T t);
}
class A implements IMessage <String>{
public String get(String t){
return "傻蛋蛋"+t;
}
}
public class Jiekou{
public static void main (String [] args){
IMessage <String> a=new A ();
System.out.println(a.get("guawawa"));
}
}
输出结果同上。
泛型方法
如果将泛型写在方法上,这样对方法就称为泛型方法。但是泛型方法不一定出现的泛型类上,类上没有定义泛型,方法中也可以单独定义泛型。
例子:
public class Jiekou{
public static void main (String [] args){
Integer a []=fun(0,1,2);
for(int x:a){
System.out.println(x+"、");
}
public static <T> T[] fun(T...agrs){
return args;
}
}
传统工厂设计:一个接口就需要一个工厂类
现代工厂:利用泛型改进工厂
interface IMessage{
public abstract void send(String str);
}
class MessageImpl implements IMessage{
public void send(String str){
System.out.println("消息发送:"+str);
}
}
class Factory{
public static <T> T get(String name,T t){
if ("a".equalsIgnoreCase(name)){
return new MessageImpl();
}
return null;
}
}
public class Jiekou{
public static void main (String [] args){
IMessage c=(IMessage) Factory.get("a",IMessage.class);
a.send("123456");
}
}