java学习之路 之 泛型及练习题

时间:2023-02-24 07:39:44

泛型

为什么要有泛型(Generic)?
1. 解决元素存储的安全性问题
2. 解决获取数据元素时,需要类型强转的问题
泛型,JDK1.5新加入的,解决数据类型的安全性问题,其主要原理是在类声明时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这样在类声明或实例化
时只要指定好需要的具体的类型即可。        
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。

使用泛型:
泛型的声明:
interface List<T> 和 class TestGen<K,V>其中,T,K,V不代表值,而是表示类型。这里使用任意字母都可以。常用T表示,是Type的缩写。
泛型的实例化:
一定要在类名后面指定类型参数的值(类型)。如:          
List<String> strList = new ArrayList<String>();        
Iterator<Customer> iterator = customers.iterator();
T只能是类,不能用基本数据类型填充。

泛型的几个重要使用:
1.在集合中使用泛型
2.自定义泛型类
3.泛型方法
4.泛型接口

对于泛型类(含集合类):

1.对象实例化时不指定泛型,默认为:Object。
2.泛型不同的引用不能相互赋值。
3.加入集合中的对象类型必须与指定的泛型类型一致。
4.静态方法中不能使用类的泛型。
5.如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。
6.不能在catch中使用泛型
7.从泛型类派生子类,泛型类型需具体化
把一个集合中的内容限制为一个特定的数据类型,这就是generics背后的核心思想。

自定义泛型类:

class Person<T>{//使用T类型定义变量 
private T info;//使用T类型定义一般方法

public T getInfo(){
return info;
}

public void setInfo(T info){
this.info = info;
}

//使用T类型定义构造器
public Person(){}

public Person(T info){
this.info = info;
}
//static的方法中不能声明泛型
//public static void show(T t){
//}
//不能在try-catch中使用泛型定义
//try{}
//catch(T t){}
}


对于泛型方法:

方法,也可以被泛型化,不管此时定义在其中的类是不是泛型化的。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。
泛型方法的格式:[访问权限]  <泛型>  返回类型  方法名([泛型标识 参数名称])  抛出的异常
public class DAO {
public <E> E get(int id, E e){
E result = null;
return result;
}
}

泛型和继承的关系:

如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G<B>并不是G<A>的子类型!
比如:String是Object的子类,但是List<String >并不是List<Object>的子类。

通配符:

1.使用类型通配符:? //只读型
比如:List<?>   ,Map<?,?>
List<?>是List<String>、List<Object>等各种泛型List的父类。
2.读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。
3.写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。
唯一的例外是null,它是所有类型的成员。


将任意元素加入到其中不是类型安全的:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // 编译时错误
因为我们不知道c的元素类型,我们不能向其中添加对象。        
add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。
唯一的例外的是null,它是所有类型的成员。
另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object

有限制的通配符:

<?>
允许所有泛型的引用调用
举例:
<? extends Number>     
(无穷小 , Number]只允许泛型为Number及Number子类的引用调用
<? super Number>      
[Number , 无穷大)只允许泛型为Number及Number父类的引用调用
<? extends Comparable>
只允许泛型为实现Comparable接口的实现类的引用调用

泛型应用:

用户在设计类的时候往往会使用类的关联关系,例如,一个人中可以定义一个信息的属性,但是一个人可能有各种各样的信息(如联系方式、基本信息等),所以

此信息属性的类型就可以通过泛型进行声明,然后只要设计相应的信息类即可。

练习题
package com.atguigu.javae.generic;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import org.junit.Test;

/**
* 自定义泛型类, 类名<X>
* 泛型和对象相关, 类型的信息最终会在创建对象时确定!
*/
class Person<X> { // X代表某类型, 既然是类型,声明变量,作为返回值...
// 当在创建对象时未指定X的具体类型, 它的类型就是Object
//private static int X x;

private X info;

public Person() {
}

public Person(X info) {
this.info = info;
}

public void setInfo(X info) {
try {
this.info = info;
//} catch (X e) { // catch 中只能捕捉Throwable及其子类 ,不可以使用类型不一定的X类型
} catch (Throwable e) { // catch 中只能捕捉Throwable及其子类
e.printStackTrace();
}
}

public X getInfo() {
return info;
}

@Override
public String toString() {
return "某人, 信息[" + info + "]";
}

/*
public static void test(X t) { // 静态环境中不允许使用和对象相关的泛型
System.out.println(t);
}*/

// 泛型方法, 在返回值前面加<类型>, 一定要在形参中带上类型信息
public static <Y> Y testY(Y y) { // 如果泛型方法中的泛型类型名和类中的一样, 则会屏蔽类中的泛型
return null;
}


}

class Student extends Person<String> { // 在Student子类中,info属性就是固定的String类型
}

public class GenericTest {

// 写一个方法参数是(Collection<? extends Comparable> coll)
// 在方法中返回这个集合中的最小值
// 写一个测试方法, 创建List集合,保存一些字符串, 再创建Set集合, 保存一些char, 分别使用这两个集合调用以上方法,打印输出结果
@SuppressWarnings({ "rawtypes", "unchecked" })
public Comparable getMin(Collection<? extends Comparable> coll) {
if (coll.size() == 0) {
return null;
}
Iterator<? extends Comparable> iterator = coll.iterator();
Comparable min = iterator.next();
while (iterator.hasNext()) {
Comparable comp = iterator.next();
if (comp.compareTo(min) < 0) {
min = comp;
}
}
return min;
}

@Test
public void test7() {
List<String> list = new LinkedList<String>();
list.add("xxx");
list.add("bbb");
list.add("aaa");
list.add("ddd");
System.out.println(getMin(list));

Collection<Character> set = new HashSet<Character>();
set.add('我');
set.add('好');
set.add('a');
set.add(' ');
System.out.println(getMin(set));
}

public double sumList(List<? extends Number> list) {
// list就可以指向以Number及其子类为泛型的集合, 但是引用不能添加元素, 但是可以获取元素,并且Number
Iterator<? extends Number> iterator = list.iterator();
double sum = 0;
while (iterator.hasNext()) {
Number number = iterator.next();
sum += number.doubleValue();
}
return sum;
}

public double getMax(Collection<? extends Number> coll) {
Iterator<? extends Number> iterator = coll.iterator();
if (!iterator.hasNext()) {
throw new RuntimeException("集合为空,没法找最大值");
}
Number maxNumber = iterator.next();
while (iterator.hasNext()) {
Number tmpNumber = iterator.next();
if (tmpNumber.doubleValue() > maxNumber.doubleValue()) {
maxNumber = tmpNumber;
}
}
return maxNumber.doubleValue();
}

// 写一个方法参数是(Collection<? extends Number> coll)
// 在方法中返回这个集合中的最大值
// 写一个测试方法, 创建List集合,保存一些整数, 再创建Set集合, 保存一些float, 分别使用这两个集合调用以上方法,打印输出结果


@Test
public void test6() {
ArrayList<Integer> list1 = new ArrayList<Integer>();
list1.add(100);
list1.add(20);
list1.add(30);
list1.add(1000);
list1.add(7);

System.out.println(getMax(list1));

Set<Float> set = new HashSet<Float>();
set.add(234.23f);
set.add(1234.23f);
set.add(3234.23f);
set.add(2334.23f);
set.add(234.23f);

System.out.println(getMax(set));
}
@Test
public void test5() {
ArrayList<Integer> list1 = new ArrayList<Integer>();
list1.add(100);
list1.add(20);
list1.add(30);
list1.add(10);
list1.add(7);

List<Double> list2 = new Vector<Double>();
list2.add(3.1);
list2.add(2.1);
list2.add(4.1);
list2.add(5.1);
list2.add(1.1);

double sum = sumList(list1);
System.out.println(sum);

sum = sumList(list2);
System.out.println(sum);
/*
List<? extends Number> list2 = list; // list2就可以指向以Number及其子类为泛型的集合, 但是引用不能添加元素, 但是可以获取元素,并且Number
Iterator<? extends Number> iterator = list2.iterator();
while (iterator.hasNext()) {
Number number = iterator.next();
sum += number.intValue();
}
System.out.println(sum);

list2 = list3;
*/
}
@Test
public void test4() {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(100);
list.add(20);
list.add(30);
list.add(10);
list.add(7);

List<?> list2 = list;// 只读访问
Object object = list2.get(1);
System.out.println(object);
}

@SuppressWarnings("unused")
@Test
public void test3() {
// 使用?通配符 通常用于只读访问
Person<?> person = new Person<>();
//person.setInfo("abc");
person.setInfo(null);
Object object = person.getInfo();
}

@SuppressWarnings({ "static-access", "rawtypes", "unchecked", "unused" })
@Test
public void test2() {
//Person<Object> person = new Person<String>("张三");
Person<Integer> person = new Person<Integer>(30);
String string = person.testY("abc");
System.out.println(string);

ArrayList list = new ArrayList();
list.add(100);
list.add(200);
list.add(50);
list.add(10);

Integer[] tmp = new Integer[0];
//Integer[] arr = list.toArray(tmp);
}

@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void test1() {
Person person = new Person(true);
Object valaueObject = person.getInfo();
System.out.println(valaueObject);

Person<String> person2 = new Person<String>("张三");
String name = person2.getInfo();
System.out.println(name);

Person<Integer> person3 = new Person<Integer>(30);
Integer integer = person3.getInfo();
System.out.println(integer);
}
}