泛型問題引出
泛型主要目的是為了解決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");
}
}