吉安市城鄉(xiāng)規(guī)劃建設(shè)局網(wǎng)站網(wǎng)絡(luò)營(yíng)銷與策劃
泛型
JDK1.5設(shè)計(jì)了泛型的概念。泛型即為“類型參數(shù)”,這個(gè)類型參數(shù)在聲明它的類、接口或方法中,代表未知的通用的類型。例如:
java.lang.Comparable接口和java.util.Comparator接口,是用于對(duì)象比較大小的規(guī)范接口,這兩個(gè)接口只是限定了當(dāng)一個(gè)對(duì)象大于另一個(gè)對(duì)象時(shí)返回正整數(shù),小于返回負(fù)整數(shù),等于返回0。但是并不確定是什么類型的對(duì)象比較大小,之前的時(shí)候只能用Object類型表示,使用時(shí)既麻煩又不安全,因此JDK1.5就給它們?cè)黾恿朔盒汀?/p>
public interface Comparable<T>{int compareTo(T o) ;
}
public interface Comparator<T>{int compare(T o1, T o2) ;
}
其中<T>就是類型參數(shù),即泛型。
泛型的好處
那么我們?cè)谑褂萌缟厦孢@樣的接口時(shí),如果沒(méi)有泛型或不指定泛型,很麻煩,而且有安全隱患。如果有了泛型并使用泛型,那么既能保證安全,又能簡(jiǎn)化代碼。
JavaBean:圓類型
class Circle{private double radius;public Circle(double radius) {super();this.radius = radius;}public double getRadius() {return radius;}public void setRadius(double radius) {this.radius = radius;}@Overridepublic String toString() {return "Circle [radius=" + radius + "]";}}
比較器
import java.util.Comparator;public class CircleComparator implements Comparator{@Overridepublic int compare(Object o1, Object o2) {//強(qiáng)制類型轉(zhuǎn)換Circle c1 = (Circle) o1;Circle c2 = (Circle) o2;return Double.compare(c1.getRadius(), c2.getRadius());}}
測(cè)試類
public class TestGeneric {public static void main(String[] args) {CircleComparator com = new CircleComparator();System.out.println(com.compare(new Circle(1), new Circle(2)));System.out.println(com.compare("圓1", "圓2"));//運(yùn)行時(shí)異常:ClassCastException}
}
使用泛型:
比較器:
class CircleComparator implements Comparator<Circle>{@Overridepublic int compare(Circle o1, Circle o2) {//不再需要強(qiáng)制類型轉(zhuǎn)換,代碼更簡(jiǎn)潔return Double.compare(o1.getRadius(), o2.getRadius());}}
測(cè)試類
import java.util.Comparator;public class TestGeneric {public static void main(String[] args) {CircleComparator com = new CircleComparator();System.out.println(com.compare(new Circle(1), new Circle(2)));// System.out.println(com.compare("圓1", "圓2"));//編譯錯(cuò)誤,因?yàn)?#34;圓1", "圓2"不是Circle類型,編譯器提前報(bào)錯(cuò),而不是冒著風(fēng)險(xiǎn)在運(yùn)行時(shí)再報(bào)錯(cuò)}
}
其中:<T>是類型變量(Type Variables),Comparator<T>這種就稱為參數(shù)化類型(Parameterized Types),Comparator<Circle>中的<Circle>是參數(shù)化類型的類型參數(shù)<Type Arguments of Parameterized Types>。
類比方法的參數(shù),我們可以把<T>,稱為類型形參,將<Circle>稱為類型實(shí)參,有助于我們理解泛型。?
參數(shù)類型:泛型類與泛型接口
當(dāng)我們?cè)诼暶黝惢蚪涌跁r(shí),類或接口中定義某個(gè)成員時(shí),該成員有些類型是不確定的,而這個(gè)類型需要在使用這個(gè)類或接口時(shí)才可以確定,那么我們可以使用泛型。
聲明泛型類與泛型接口
語(yǔ)法格式:
【修飾符】 class 類名<類型變量列表>{}
【修飾符】 interface 接口名<類型變量列表>{}
<類型變量列表>:可以是一個(gè)或多個(gè)類型變量,一般都是使用單個(gè)的大寫(xiě)字母表示。例如:<T>、<K,V>等。
<類型變量列表>中的類型變量不能用于靜態(tài)成員上。
示例代碼:
例如:我們要聲明一個(gè)學(xué)生類,該學(xué)生包含姓名、成績(jī),而此時(shí)學(xué)生的成績(jī)類型不確定,為什么呢,因?yàn)?#xff0c;語(yǔ)文老師希望成績(jī)是“優(yōu)秀”、“良好”、“及格”、“不及格”,數(shù)學(xué)老師希望成績(jī)是89.5, 65.0,英語(yǔ)老師希望成績(jī)是'A','B','C','D','E'。那么我們?cè)谠O(shè)計(jì)這個(gè)學(xué)生類時(shí),就可以使用泛型。
public class Student<T>{private String name;private T score;public Student() {super();}public Student(String name, T score) {super();this.name = name;this.score = score;}public String getName() {return name;}public void setName(String name) {this.name = name;}public T getScore() {return score;}public void setScore(T score) {this.score = score;}@Overridepublic String toString() {return "姓名:" + name + ", 成績(jī):" + score;}
}
使用泛型類與泛型接口
在使用這種參數(shù)化的類與接口時(shí),我們需要指定泛型變量的實(shí)際類型參數(shù):
(1)實(shí)際類型參數(shù)必須是引用數(shù)據(jù)類型,不能是基本數(shù)據(jù)類型
(2)在創(chuàng)建類的對(duì)象時(shí)指定類型變量對(duì)應(yīng)的實(shí)際類型參數(shù)
public class TestGeneric{public static void main(String[] args) {//語(yǔ)文老師使用時(shí):Student<String> stu1 = new Student<String>("張三", "良好");//數(shù)學(xué)老師使用時(shí)://Student<double> stu2 = new Student<double>("張三", 90.5);//錯(cuò)誤,必須是引用數(shù)據(jù)類型Student<Double> stu2 = new Student<Double>("張三", 90.5);//英語(yǔ)老師使用時(shí):Student<Character> stu3 = new Student<Character>("張三", 'C');//錯(cuò)誤的指定//Student<Object> stu = new Student<String>();//錯(cuò)誤的}
}
JDK1.7支持簡(jiǎn)寫(xiě)形式:Student<String> stu1 = new Student<>("張三", "良好");
指定泛型實(shí)參時(shí),必須左右兩邊一致,不存在多態(tài)現(xiàn)象
(3)在繼承泛型類或?qū)崿F(xiàn)泛型接口時(shí),指定類型變量對(duì)應(yīng)的實(shí)際類型參數(shù)
class ChineseStudent extends Student<String>{public ChineseStudent() {super();}public ChineseStudent(String name, String score) {super(name, score);}}
public class TestGeneric{public static void main(String[] args) {//語(yǔ)文老師使用時(shí):ChineseStudent stu = new ChineseStudent("張三", "良好");}
}
class Circle implements Comparable<Circle>{private double radius;public Circle(double radius) {super();this.radius = radius;}public double getRadius() {return radius;}public void setRadius(double radius) {this.radius = radius;}@Overridepublic String toString() {return "Circle [radius=" + radius + "]";}@Overridepublic int compareTo(Circle c){return Double.compare(radius,c.radius);}}
類型變量的上限
當(dāng)在聲明類型變量時(shí),如果不希望這個(gè)類型變量代表任意引用數(shù)據(jù)類型,而是某個(gè)系列的引用數(shù)據(jù)類型,那么可以設(shè)定類型變量的上限。
語(yǔ)法格式:
<類型變量 extends 上限>
如果有多個(gè)上限
<類型變量 extends 上限1 & 上限2>
如果多個(gè)上限中有類有接口,那么只能有一個(gè)類,而且必須寫(xiě)在最左邊。接口的話,可以多個(gè)。
如果在聲明<類型變量>時(shí)沒(méi)有指定任何上限,默認(rèn)上限是java.lang.Object。
例如:我們要聲明一個(gè)兩個(gè)數(shù)求和的工具類,要求兩個(gè)加數(shù)必須是Number數(shù)字類型,并且實(shí)現(xiàn)Comparable接口。
class SumTools<T extends Number & Comparable<T>>{private T a;private T b;public SumTools(T a, T b) {super();this.a = a;this.b = b;}@SuppressWarnings("unchecked")public T getSum(){if(a instanceof BigInteger){return (T) ((BigInteger) a).add((BigInteger)b);}else if(a instanceof BigDecimal){return (T) ((BigDecimal) a).add((BigDecimal)b);}else if(a instanceof Short){return (T)(Integer.valueOf((Short)a+(Short)b));}else if(a instanceof Integer){return (T)(Integer.valueOf((Integer)a+(Integer)b));}else if(a instanceof Long){return (T)(Long.valueOf((Long)a+(Long)b));}else if(a instanceof Float){return (T)(Float.valueOf((Float)a+(Float)b));}else if(a instanceof Double){return (T)(Double.valueOf((Double)a+(Double)b));}throw new UnsupportedOperationException("不支持該操作");}
}
測(cè)試類
public static void main(String[] args) {SumTools<Integer> s = new SumTools<Integer>(1,2);Integer sum = s.getSum();System.out.println(sum);// SumTools<String> s = new SumTools<String>("1","2");//錯(cuò)誤,因?yàn)镾tring類型不是extends Number}
泛型擦除
當(dāng)使用參數(shù)化類型的類或接口時(shí),如果沒(méi)有指定泛型,那么會(huì)怎么樣呢?
會(huì)發(fā)生泛型擦除,自動(dòng)按照最左邊的第一個(gè)上限處理。如果沒(méi)有指定上限,上限即為Object。
public static void main(String[] args) {SumTools s = new SumTools(1,2);Number sum = s.getSum();System.out.println(sum);}
import java.util.Comparator;public class CircleComparator implements Comparator{@Overridepublic int compare(Object o1, Object o2) {//強(qiáng)制類型轉(zhuǎn)換Circle c1 = (Circle) o1;Circle c2 = (Circle) o2;return Double.compare(c1.getRadius(), c2.getRadius());}}
public class TestExer1 {public static void main(String[] args) {Coordinate<String> c1 = new Coordinate<>("北緯38.6", "東經(jīng)36.8");System.out.println(c1);// Coordinate<Double> c2 = new Coordinate<>(38.6, 38);//自動(dòng)裝箱與拆箱只能與對(duì)應(yīng)的類型 38是int,自動(dòng)裝為IntegerCoordinate<Double> c2 = new Coordinate<>(38.6, 36.8);System.out.println(c2);}
}
class Coordinate<T>{private T x;private T y;public Coordinate(T x, T y) {super();this.x = x;this.y = y;}public Coordinate() {super();}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;}@Overridepublic String toString() {return "Coordinate [x=" + x + ", y=" + y + "]";}}
泛型方法
前面介紹了在定義類、接口時(shí)可以聲明<類型變量>,在該類的方法和屬性定義、接口的方法定義中,這些<類型變量>可被當(dāng)成普通類型來(lái)用。但是,在另外一些情況下,
(1)如果我們定義類、接口時(shí)沒(méi)有使用<類型變量>,但是某個(gè)方法定義時(shí),想要自己定義<類型變量>;
(2)另外我們之前說(shuō)類和接口上的類型形參是不能用于靜態(tài)方法中,那么當(dāng)某個(gè)靜態(tài)方法想要定義<類型變量>。
那么,JDK1.5之后,還提供了泛型方法的支持。
語(yǔ)法格式:
【修飾符】 <類型變量列表> 返回值類型 方法名(【形參列表】)【throws 異常列表】{//...
}
<類型變量列表>:可以是一個(gè)或多個(gè)類型變量,一般都是使用單個(gè)的大寫(xiě)字母表示。例如:<T>、<K,V>等。
<類型變量>同樣也可以指定上限
示例代碼:
我們編寫(xiě)一個(gè)數(shù)組工具類,包含可以給任意對(duì)象數(shù)組進(jìn)行從小到大排序,要求數(shù)組元素類型必須實(shí)現(xiàn)Comparable接口
public class MyArrays{public static <T extends Comparable<T>> void sort(T[] arr){for (int i = 1; i < arr.length; i++) {for (int j = 0; j < arr.length-i; j++) {if(arr[j].compareTo(arr[j+1])>0){T temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}}
}
測(cè)試類
public class TestGeneric{public static void main(String[] args) {int[] arr = {3,2,5,1,4};
// MyArrays.sort(arr);//錯(cuò)誤的,因?yàn)閕nt[]不是對(duì)象數(shù)組String[] strings = {"hello","java","chai"};MyArrays.sort(strings);System.out.println(Arrays.toString(strings));Circle[] circles = {new Circle(2.0),new Circle(1.2),new Circle(3.0)};MyArrays.sort(circles);System.out.println(Arrays.toString(circles));}
}
類型通配符
當(dāng)我們聲明一個(gè)方法時(shí),某個(gè)形參的類型是一個(gè)參數(shù)化的泛型類或泛型接口類型,但是在聲明方法時(shí),又不確定該泛型實(shí)際類型,我們可以考慮使用類型通配符。
<?>任意類型
例如:我們要聲明一個(gè)學(xué)生管理類,這個(gè)管理類要包含一個(gè)方法,可以遍歷學(xué)生數(shù)組。
學(xué)生管理類:
class StudentService {public static void print(Student<?>[] arr) {for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}}
}
測(cè)試類
public class TestGeneric {public static void main(String[] args) {// 語(yǔ)文老師使用時(shí):Student<String> stu1 = new Student<String>("張三", "良好");// 數(shù)學(xué)老師使用時(shí):// Student<double> stu2 = new Student<double>("張三", 90.5);//錯(cuò)誤,必須是引用數(shù)據(jù)類型Student<Double> stu2 = new Student<Double>("張三", 90.5);// 英語(yǔ)老師使用時(shí):Student<Character> stu3 = new Student<Character>("張三", 'C');Student<?>[] arr = new Student[3];arr[0] = stu1;arr[1] = stu2;arr[2] = stu3;StudentService.print(arr);}
}
<? extends 上限>
例如:我們要聲明一個(gè)學(xué)生管理類,這個(gè)管理類要包含一個(gè)方法,找出學(xué)生數(shù)組中成績(jī)最高的學(xué)生對(duì)象。
要求學(xué)生的成績(jī)的類型必須可比較大小,實(shí)現(xiàn)Comparable接口。
學(xué)生管理類:
class StudentService {@SuppressWarnings({ "rawtypes", "unchecked" })public static Student<? extends Comparable> max(Student<? extends Comparable>[] arr){Student<? extends Comparable> max = arr[0];for (int i = 0; i < arr.length; i++) {if(arr[i].getScore().compareTo(max.getScore())>0){max = arr[i];}}return max;}
}
測(cè)試類
public class TestGeneric {@SuppressWarnings({ "rawtypes", "unchecked" })public static void main(String[] args) {Student<? extends Double>[] arr = new Student[3];arr[0] = new Student<Double>("張三", 90.5);arr[1] = new Student<Double>("李四", 80.5);arr[2] = new Student<Double>("王五", 94.5);Student<? extends Comparable> max = StudentService.max(arr);System.out.println(max);}
}
<? super 下限>
現(xiàn)在要聲明一個(gè)數(shù)組工具類,包含可以給任意對(duì)象數(shù)組進(jìn)行從小到大排序,只要你指定定制比較器對(duì)象,而且這個(gè)定制比較器對(duì)象可以是當(dāng)前數(shù)組元素類型自己或其父類的定制比較器對(duì)象
數(shù)組工具類:
class MyArrays{public static <T> void sort(T[] arr, Comparator<? super T> c){for (int i = 1; i < arr.length; i++) {for (int j = 0; j < arr.length-i; j++) {if(c.compare(arr[j], arr[j+1])>0){T temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}}
}
例如:有如下JavaBean
class Person{private String name;private int age;public Person(String name, int age) {super();this.name = name;this.age = age;}public Person() {super();}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "name=" + name + ", age=" + age;}
}
class Student extends Person{private int score;public Student(String name, int age, int score) {super(name, age);this.score = score;}public Student() {super();}public int getScore() {return score;}public void setScore(int score) {this.score = score;}@Overridepublic String toString() {return super.toString() + ",score=" + score;}}
測(cè)試類
public class TestGeneric {public static void main(String[] args) {Student[] all = new Student[3];all[0] = new Student("張三", 23, 89);all[1] = new Student("李四", 22, 99);all[2] = new Student("王五", 25, 67);MyArrays.sort(all, new Comparator<Person>() {@Overridepublic int compare(Person o1, Person o2) {return o1.getAge() - o2.getAge();}});System.out.println(Arrays.toString(all));MyArrays.sort(all, new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {return o1.getScore() - o2.getScore();}});System.out.println(Arrays.toString(all));}
}
使用類型通配符來(lái)指定類型參數(shù)的問(wèn)題
<?>:不可變,因?yàn)?lt;?>類型不確定,編譯時(shí),任意類型都是錯(cuò)
<? extends 上限>:不可變,因?yàn)?lt;? extends 上限>的?可能是上限或上限的子類,即類型不確定,編譯按任意類型處理都是錯(cuò)。
<? super 下限>:可以將值修改為下限或下限子類的對(duì)象,因?yàn)?lt;? super 下限>?代表是下限或下限的父類,那么設(shè)置為下限或下限子類的對(duì)象是安全的。
public class TestGeneric {public static void main(String[] args) {Student<?> stu1 = new Student<>();stu1.setScore(null);//除了null,無(wú)法設(shè)置為其他值Student<? extends Number> stu2 = new Student<>();stu2.setScore(null);//除了null,無(wú)法設(shè)置為其他值Student<? super Number> stu3 = new Student<>();stu3.setScore(56);//可以設(shè)置Number或其子類的對(duì)象}
}
class Student<T>{private String name;private T score;public Student() {super();}public Student(String name, T score) {super();this.name = name;this.score = score;}public String getName() {return name;}public void setName(String name) {this.name = name;}public T getScore() {return score;}public void setScore(T score) {this.score = score;}@Overridepublic String toString() {return "姓名:" + name + ", 成績(jī):" + score;}
}