Java泛型01 1.泛型的理解和好處 看一個需求: 請編寫程式,在ArrayList中添加三個Dog對象 Dog對象含有name和age,並輸出name和age(要求使用getXXX()) 先用傳統的方法來解決 >引出泛型 傳統的方法: package li.generic; import jav ...
Java泛型01
1.泛型的理解和好處
看一個需求:
- 請編寫程式,在ArrayList中添加三個Dog對象
- Dog對象含有name和age,並輸出name和age(要求使用getXXX())
先用傳統的方法來解決--->引出泛型
傳統的方法:
package li.generic;
import java.util.ArrayList;
@SuppressWarnings("all")
public class Introduce_ {
public static void main(String[] args) {
//用傳統的方法來解決
ArrayList arrayList = new ArrayList();
arrayList.add(new Dog("旺財",10));
arrayList.add(new Dog("發財",1));
arrayList.add(new Dog("小黃",5));
for (Object o:arrayList) {
//向下轉型
Dog dog = (Dog) o;
System.out.println(dog.getName()+"-"+dog.getAge());
}
}
}
class Dog {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
}
//假設,我們的程式員不小心添加了一隻貓
arrayList.add(new Cat("招財貓",8));
那麼 在使用增強for迴圈輸出的時候向下轉型時就會拋出異常:類型轉換錯誤
使用傳統方法問題的分析:
- 不能對加入到集合ArrayList中的數據進行約束(不安全)
- 遍歷的時候,需要進行類型轉換,如果集合中的數據量較大,對效率有影響
使用泛型來解決問題:
package li.generic;
import java.util.ArrayList;
@SuppressWarnings("all")
public class Introduce_ {
public static void main(String[] args) {
//使用泛型
// 1. 當我們這樣寫的時候:ArrayList<Dog> 表示集合ArrayList中的元素是Dog類型
// 2. 如果編譯器發現添加的類型不滿足要求,就會報錯
// 3.在遍歷的時候,可以直接取出Dog類型而不是Object
ArrayList<Dog> arrayList = new ArrayList<Dog>();
arrayList.add(new Dog("旺財",10));
arrayList.add(new Dog("發財",1));
arrayList.add(new Dog("小黃",5));
//假設,我們的程式員不小心添加了一隻貓,就會報錯
// arrayList.add(new Cat("招財貓",8));
System.out.println("====使用泛型====");
for (Dog dog:arrayList) {
System.out.println(dog.getName()+"-"+dog.getAge());
}
}
}
class Dog {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
}
class Cat {
private String name;
private int age;
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
}
package li.generic;
import java.util.ArrayList;
@SuppressWarnings("all")
public class Introduce_ {
public static void main(String[] args) {
//使用泛型
// 1. 當我們這樣寫的時候:ArrayList<Dog> 表示集合ArrayList中的元素是Dog類型
// 2. 如果編譯器發現添加的類型不滿足要求,就會報錯
// 3.在遍歷的時候,可以直接取出Dog類型,而不是Object
ArrayList<Dog> arrayList = new ArrayList<Dog>();
arrayList.add(new Dog("旺財",10));
arrayList.add(new Dog("發財",1));
arrayList.add(new Dog("小黃",5));
//假設,我們的程式員不小心添加了一隻貓,就會報錯
// arrayList.add(new Cat("招財貓",8));
System.out.println("====使用泛型====");
for (Dog dog:arrayList) {
System.out.println(dog.getName()+"-"+dog.getAge());
}
}
}
class Dog {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
}
class Cat {
private String name;
private int age;
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
}
泛型的好處:
-
編譯時,檢查添加元素的類型,提高了安全型
-
減少了類型轉換的次數,提高效率
如上面例子所示:不使用泛型的時候,Dog對象放到ArrayList里會先轉成Object類型,在取出的時候還要再轉換成Dog類型(Dog--加入-->Object--取出-->Dog)
使用了泛型,則放入和取出時都不需要類型轉換,提高效率(Dog-->-Dog-->Dog)
-
不再提示編譯警告
不添加
@SuppressWarnings("all")
編譯器也不再警告
2.泛型介紹
泛型是一種可以表示數據類型的 數據類型
如下圖:public class ArrayList<E>{}
E 稱為泛型
泛(廣泛)型(類型)===>integer,String,Dog,……
- 泛型又稱參數化類型,是jdk5.0出現的新特性,解決數據類型的安全性問題
- 在類聲明或者實例化時只要指定好需要的具體類型即可
- Java泛型可以保證如果程式在編譯時沒有發出警告,運行就不會產生ClassCastException異常。同時,代碼更加簡潔、健壯
- 泛型的作用是:可以在類聲明時 通過一個標識 表示類中的某個屬性,或者是某個方法的返回值的類型,或者是參數類型
例子:
package li.generic;
public class Generic03 {
public static void main(String[] args) {
Person<String> person = new Person<String>("jack");
person.showCalss();//class java.lang.String
/*
可以這樣理解:上面的Person類變為了
class Person{
String s;
public Person(String s) {
this.s = s;
}
public String f() {
return s;
}
}
*/
Person<Integer> person1 = new Person<Integer>(100);
person1.showCalss();//class java.lang.Integer
/* 可以這樣理解:上面的Person類變為了
class Person{
Integer s;
public Person(Integer s) {
this.s = s;
}
public Integer f() {
return s;
}
}
*/
}
}
class Person<E> {
E s; // 用 E表示 s的數據類型,該數據類型在定義 Person對象的時候指定,即在編譯期間,就確定 E是什麼類型
public Person(E s) {//E也可以是參數類型
this.s = s;
}
public E f() {//返回類型使用E
return s;
}
public void showCalss(){
System.out.println(s.getClass());//顯示s的運行類型
}
}
註意:E的數據類型在定義 Person 對象的時候指定,即在編譯期間,就確定E是什麼類型
泛型是一種可以表示數據類型的 數據類型
3.泛型的語法
3.1泛型的聲明
interface 介面<T>{}
和class 類<K,V>{}//比如:List、ArrayList
說明:
1)其中,T,K,V不代表值,而是表示類型
2)任意字母都可以。常用T表示,是Type的縮寫
3.2泛型的實例化
要在類名後面指定類型參數的值(類型),如:
(1)List<String> strList = new ArrayList<String>() ;
(2)Iterator<Customer> iterator = customer.iterator();
3.3泛型使用舉例
例子:泛型使用舉例:
練習:
- 創建三個學生對象
- 學生對象放入到HashSet中使用
- 放入到HashMap中,要求Key是String name ,Value就是學生對象
- 使用兩種方法遍歷
練習:
package li.generic;
import java.util.*;
public class GenericExercise {
public static void main(String[] args) {
//使用泛型的方法給HashSet放入三個學生對象
HashSet<Student> students = new HashSet<Student>();
students.add(new Student("jack", 18));
students.add(new Student("marry", 17));
students.add(new Student("link", 123));
//使用HashSet的增強for
System.out.println("===使用HashSet的增強for===");
for (Student student : students) {
System.out.println(student);
}
//使用泛型的方法給HashMap放入三個學生對象
HashMap<String, Student> hm = new HashMap<String, Student>();
hm.put("jack", new Student("jack", 18));
hm.put("lucy", new Student("lucy", 28));
hm.put("olin", new Student("olin", 16));
//迭代器 EntrySet
System.out.println("===迭代器 EntrySet===");
Set<Map.Entry<String,Student>> entries = hm.entrySet();
Iterator<Map.Entry<String,Student>> iterator1 = entries.iterator();
while (iterator1.hasNext()) {
Map.Entry<String, Student> next = iterator1.next();
System.out.println(next.getKey()+"-"+next.getValue());
}
}
}
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
3.4泛型使用的註意事項和細節
-
interface List<T>{} ,public class HashSet<E>{}..等等
說明:T,E只能是引用類型
看看下麵語句是否正確?
List<Integer> list = new ArrayList<Integer>();//正確
List<int> list2 = new ArrayList<int>();//錯誤
-
在指定泛型具體類型後,可以傳入該類型或者其子類類型
-
泛型使用形式
3.1 在實際的開發中,我們往往簡寫,編譯器會進行類型推斷,推薦使用下麵的寫法
ArrayList<Integer> list1 = new ArrayList<>();
3.2 泛型預設是Object類型,即如果沒有給泛型指定類型,預設就是Object:
ArrayList arrayList = new ArrayList();//等價為 ArrayList<Object> arrayList = new ArrayList<>();
例子:
package li.generic;
import java.util.ArrayList;
import java.util.List;
public class GenericDetail {
public static void main(String[] args) {
//1.給泛型指向的數據類型要求是引用類型,不能是基本數據類型
List<Integer> list = new ArrayList<Integer>();//ok
//List<int> list2 = new ArrayList<int>();錯誤
//2.因為 E指定了A類型,構造器傳入了 new A()
Pig<A> aPig = new Pig<A>(new A());//將A類型賦給泛型E,說明Pig構造器可以接收的是A類型的對象
//在指定泛型具體類型後,可以傳入該類型或者其子類類型
Pig<A> aPig2 = new Pig<A>(new B());
aPig.showClass();//class li.generic.A
aPig2.showClass();//class li.generic.B
// 3.泛型的使用形式
//在實際的開發中,我們往往簡寫,編譯器會進行類型推斷,推薦使用下麵的寫法
ArrayList<Integer> list1 = new ArrayList<>();
}
}
class A{}
class B extends A{}
class Pig<E>{
E e;
public Pig(E e) {
this.e = e;
}
public void showClass(){
System.out.println(e.getClass());//運行類型
}
}
4.泛型課堂練習
定義Employee類
-
該類包括:private成員變數name,sal,birthday,其中birthday為MyDate類的對象;
-
為每一個屬性定義getter、setter方法;
-
重寫toString方法輸出name,sal,birthday;
-
MyDate類包括:private成員變數year,month,day。併為為每一個屬性定義getter、setter方法;
-
創建該類的3個對象,並把這些對象放入ArrayList集合中(ArrayList需使用泛型來定義),對集合中的元素進行排序,並遍歷輸出:
排序方式:調用ArrayList的sort方法,傳入Comparator對象(使用泛型),先按照name排序,如果name相同,則按照生日日期的先後排序。(即定製排序)
練習:
package li.generic;
import java.util.ArrayList;
import java.util.Comparator;
public class GenericHomework {
public static void main(String[] args) {
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("tom", 20000, new MyDate(1980, 12, 11)));
employees.add(new Employee("jack", 12000, new MyDate(2001, 12, 12)));
employees.add(new Employee("tom", 50000, new MyDate(1980, 12, 10)));
employees.sort(new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
// //比較name
// int i = o1.getName().compareTo(o2.getName());
// if (i != 0) {
// return i;
// }
// //如果name相同,就比較birthday-year
// int yearMinus = o1.getBirthday().getYear()-o2.getBirthday().getYear();
// if (yearMinus !=0) {
// return yearMinus;
// }
// //如果year相同,就比較month
// int monthMinus = o1.getBirthday().getMonth()-o2.getBirthday().getMonth();
// if (monthMinus !=0) {
// return monthMinus;
// }
// //如果month相同,就比較mday
// return o1.getBirthday().getDay()-o2.getBirthday().getDay();
//比較name
int i = o1.getName().compareTo(o2.getName());
if (i != 0) {
return i;
}
//下麵是對birthday的比較,因此,我們最好把日期的比較放到MyDate類完成
//封裝後的維護性和復用性更好
return o1.getBirthday().compareTo(o2.getBirthday());
}
});
for (Employee e : employees) {
System.out.println(e);
}
}
}
class Employee {
private String name;
private int sal;
private MyDate birthday;
public Employee(String name, int sal, MyDate birthday) {
this.name = name;
this.sal = sal;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSal() {
return sal;
}
public void setSal(int sal) {
this.sal = sal;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", sal=" + sal +
", birthday=" + birthday +
'}';
}
}
class MyDate implements Comparable<MyDate>{
private int year;
private int month;
private int day;
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
@Override
public String toString() {
return "MyDate{" +
"year='" + year + '\'' +
", month='" + month + '\'' +
", day='" + day + '\'' +
'}';
}
@Override
public int compareTo(MyDate o) {//把 年 月 日 的比較挪到這裡
//如果name相同,就比較birthday-year
int yearMinus = year-o.getYear();
if (yearMinus !=0) {
return yearMinus;
}
//如果year相同,就比較month
int monthMinus = month-o.getMonth();
if (monthMinus !=0) {
return monthMinus;
}
//如果month相同,就比較mday
return day-o.getDay();
}
}