集合【泛型】
泛型部分知识思维导图
1、泛型介绍
1.1、为什么会出现泛型?
集合中是可以存放任意对象的,只要把对象丢尽集合后,那么这时他们都会被提升成Object类型
。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换
。
但是由于集合中什么类型的元素都可以存储。导致取出时,如果出现强转就会引发运行时 ClassCastException。
而因为集合存放的数据类型不固定,故网集合里面存放元素时,存在安全隐患,如果定义集合时,可以像定义数组一样指定数据类型,那么就可以解决该类安全问题。
JDK1.5以后,出现了解决方案,使用容器时,必须明确容器中元素的类型
。这种机制称之为 :泛型
。泛型是一个类型安全机制。
1.2、泛型定义格式
<数据类型>
:往括号里面写东西其实就是在传递参数。
1.3、泛型的好处
- 安全机制。
- 将运行时期的
ClassCastException
,转移到了编译时期变成了编译失败。 - 泛型技术,是给编译器使用的技术。
避免了强转
的麻烦。
public class GenericDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("itcast");
//list.add(5);//当集合明确类型后,存放类型不一致就会编译报错
//集合已经明确具体存放的元素类型,那么在使用迭代器的时候,迭代器也同样会知道具体遍历元素类型
Iterator<String> it = list.iterator();
while(it.hasNext()){
String str = it.next();
System.out.println(str.length());
}
}
}
1.4、什么时候定义泛型?
泛型在集合框架中很常见,只要见到< >就定义泛型
。说简单点就是< >就是用来接收类型
的。
当使用集合是,将集合中存储的数据类型作为参数传递到<>中即可。
1.5、泛型简单使用
既然泛型了解泛型特点后,那么我们现在简单的使用下泛型。
定义容器,要求容器中元素唯一
,并且按指定的方法排序
。
思路:
- 元素唯一,那么只能使用Set集合
- 还要对元素排序,那么就使用TreeSet集合
- 还要按照指定方式排序,那么就要让集合自定具备比较功能,即就是要让要给集合传递一个比较器。
public class GenericDemo {
public static void main(String[] args) {
// 定义容器,要求容器中元素唯一,并且按指定的方法排序。并且使用匿名内部类的形式来完成比较器传递
TreeSet<String> set = new TreeSet<String>(new Comparator<String>(){
@Override
public int compare(String o1, String o2) {
int temp = o1.length() - o2.length();
return temp == 0 ? o1.compareTo(o2):temp;
}
});
set.add("abc");
set.add("bbb");
set.add("sh");
set.add("itcast");
for (String str : set) {
System.out.println(str);
}
}
}
2、泛型的使用
2.1、泛型类
泛型类就是在使用这个类的时候,我们就应该知道我们要设置的是什么类型的数据
,当然明确了设置,那么获取到的也是相应的类型。
class Tool<T>{
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
public class GenericTest01 {
public static void main(String[] args) {
Tool<String> t = new Tool<String>();
t.setObj("itcast");
String s = t.getObj();
System.out.println(s);
}
}
2.2、泛型方法
我们把泛型都定义在类上了,也就是说在创建类的对象时候,需要明确具体的类型,但是这个局限性很大,如果我们需要在使用类中某个方法时才能明确具体的类型那怎么办呢?
这时我们可以把泛型定义方法上
,这样的方法就称为泛型方法。
class Utils<W>{
//打印功能的方法,可以打印任意类型,把泛型定义方法上
public <T> void print(T t){
System.out.println(t);
}
//如果方法是静态,那么方法上是无法使用类上的泛型,因为类上的泛型是随着对象的创建才明确出来的
public static <Q> void show(Q q){
System.out.println("show.."+q);
}
//也可以使用类上的泛型
public void method(W w){
System.out.println(w);
}
}
2.3、泛型接口
泛型也可以定在接口中,定义在接口中的泛型,再使用的时候,如果子类已经明确具体的类型,那么子类在实现的时候就把类型明确出来,如果子类不明确具体类型,需要子类创建对象时才能明确
,这时在子类描述时可以在子类上继续加泛型。
public class GenericDemo2 {
public static void main(String[] args) {
new InterImpl().show();
new InterImpl2<String>().show();
}
}
interface Inter<E>{
void show();
}
//子类明确具体类型
class InterImpl implements Inter<String>{
public void show(){
System.out.println("show run");
}
}
//如果子类不明确具体数据类型,这时可以在子类上继续使用泛型
class InterImpl2<T> implements Inter<T>{
public void show(){
System.out.println("show run");
}
}
3、泛型高级应用
3.1、泛型通配符
当使用泛型类或者接口时,传递的具体的类型不确定,可以通过通配符(?
)表示。但是一旦使用泛型的通配符机制后,只能使用Object
类中的共性方法,集合中元素自身方法无法使用。
public class GenericDemo2 {
public static void main(String[] args) {
Set<Student> set = new HashSet<Student>();
set.add(new Student("张三",31));
set.add(new Student("李四",23));
set.add(new Student("王五",21));
List<Worker> list = new ArrayList<Worker>();
list.add(new Worker("小强", 45));
list.add(new Worker("华安", 41));
list.add(new Worker("大明", 47));
List<String> list2 = new ArrayList<String>();
list2.add("小强");
list2.add("华安");
list2.add("大明");
printCollection(set);
printCollection(list);
printCollection(list2);
}
public static void printCollection(Collection<?> list) {
for (Iterator<?> it = list.iterator(); it.hasNext();) {
System.out.println(it.next());
}
}
}
3.2、泛型限定
上述打印集合的功能,看似很强大,可以打印任意集合,可是问题也来了。如果想要对被打印的集合中的元素类型进行限制,只在指定的一些类型,进行打印。
要解决这个问题,我们就要用到泛型限定
。
只需要打印学生和工人的集合。找到学生和工人的共性类型Person。
- 泛型的限定可以这样书写:
? extends Person : 接收Person类型或者Person的子类型。
上述这种限定称为:限定泛型的上限:? extends E:接收E类型或者E的子类型。
当然有上限,肯定也有泛型的下限:? super E:接收E类型或者E的父类型。
3.3、泛型应用
案例:
获取集合中元素的最大值。
思路:
- 定义变量记录每次比较后较大的值,初始化元素中任意一个。
- 遍历容器
- 在遍历中和变量中记录的元素进行比较。并将较大的值记录到变量中。
- 遍历结束,变量中记录的就是最大值。
代码演示:
public class GenericDemo {
public static void main(String[] args) {
//1.创建一个集合Collection
Collection<Student> c1 = new ArrayList<Student>();
c1.add(new Student("张三",5));
c1.add(new Student("李四",15));
c1.add(new Student("王五",7));
c1.add(new Student("赵六",12));
//2.定义一个方法,明确具体元素类型为Student的方法getMax_1
Student stuMax = getMax_1(c1);
System.out.println("Student中的最大值"+stuMax);
//3.由于getMax_1的方法,只能传入Student类型的元素,扩展性不好.重写方法getMax_2
//用Obeject类型,可以传入任何类型的参数
Collection objList = new ArrayList();
objList.add(new Worker("宋",35));
objList.add(new Worker("马",25));
objList.add(new Worker("金",28));
// objList.add("string"); 方法2是解决了类型的局限性,甚至1个集合传入两个数据类型,造成运行时异常
Object objMax = getMax_2(objList);
System.out.println("Object中的最大值"+objMax);
//4.为了解决方法1的局限性,方法2的不稳定因素,方法3加入泛型,定义上限,下限
Collection<Worker> c2 = new ArrayList<Worker>();
c2.add(new Worker("宋",35));
c2.add(new Worker("马",25));
c2.add(new Worker("金",28));
Student stumax3 = getMax_3(c1);
Worker wokermax3 = getMax_3(c2);
Object objmax3 = getMax_3(objList);
System.out.println("Student中的最大值"+stumax3);
System.out.println("Worker中的最大值"+wokermax3);
System.out.println("Object中的最大值"+objmax3);
}
/**
* 方法3:可以接收任何类型的集合取最大值的方法.
* 注意:取最大值,需要comparTo方法的主持,所以传入的对象,必须具备比较方法,即必须实现Comparable
* 接口的对象才行.或者说,这个对象类本身并不具备比较方法,但是其父类具备比较方法,那么可以
* 这样限定<? super 需要用比较方法的那个对象>,这个限定放在什么位置?
* 注意:静态方法,就不能把这个泛型参数放在类上了,而是放在方法上.
* 其次,传入对象的类型可以是这个对象或者这个对象的子类型.<? extends 这个对象>
*
*/
public static <T extends Comparable<? super T>> T getMax_3(Collection<? extends T> c1) {
Iterator<? extends T> it = c1.iterator();
T ifMax = it.next();
while(it.hasNext()){
T temp = it.next();
if(temp.compareTo(ifMax)>0){
ifMax = temp;
}
}
return ifMax;
}
/**
* 对方法1进行了改造,能接收所有类型的集合,求出最值并返回
* 但是思考?还有没有弊端呢?
* @param objList
* @return
*/
public static Object getMax_2(Collection objList) {
Iterator it = objList.iterator();
Object ifMax = it.next();
while(it.hasNext()){
//此处temp必须用Comparable接口的子类型才行,因为需要compareTo方法
Comparable temp =(Comparable) it.next();
if(temp.compareTo(ifMax)>0){
ifMax = temp;
}
}
return ifMax;
}
/**
* 按要求1完成
* 求Student集合中最大值的方法
* 但是有局限性,传入其他不是Student类型的集合无法获取最大值.
* @param c1 限定传入Student类型的Collection集合.
* @return Student 返回Student类型的最大值.
*/
public static Student getMax_1(Collection<Student> c1) {
Iterator<Student> it = c1.iterator();
Student ifMax = it.next();
while(it.hasNext()){
Student temp = it.next();
if(temp.compareTo(ifMax)>0){
ifMax = temp;
}
}
return ifMax;
}
}
class Student implements Comparable{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Object o) {
if(!(o instanceof Student)){
throw new ClassCastException("死鬼,类型错了");
}
Student s = (Student)o;
int temp = this.age - s.age;
//以年龄为主
return temp == 0 ? this.name.compareTo(s.name) : temp;
//以名字为主
//return this.name.compareTo(s.name) == 0 ? temp : this.name.compareTo(s.name);
}
}
class Worker implements Comparable{
private String name;
private int age;
public Worker() {
}
public Worker(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Worker{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Object o) {
if(!(o instanceof Worker)){
throw new ClassCastException("死鬼,类型错了");
}
Worker w = (Worker)o;
int temp = this.age - w.age;
//以年龄为主
return temp == 0 ? this.name.compareTo(w.name) : temp;
//以名字为主
//return this.name.compareTo(w.name) == 0 ? temp : this.name.compareTo(w.name);
}
}
运行结果:
Student中的最大值Student{name=‘李四’, age=15}
Object中的最大值Worker{name=‘宋’, age=35}
Student中的最大值Student{name=‘李四’, age=15}
Worker中的最大值Worker{name=‘宋’, age=35}
Object中的最大值Worker{name=‘宋’, age=35}