泛型
泛型的概念
泛型(Generics)是指在类定义时不指定类中信息的具体数据类型,而是用一个标识符来代替,当外部实例化对象时来指定具体的数据类型。有了泛型,我们就可以在定义类或者接口时不明确指定类中的具体数据类型,在实例化时再来指定具体的数据类型。这样极大地提高了类的扩展性,一个类可以装载不同的数据类型,泛型可以指代类中的成员变量数据类型,方法的返回值数据类型以及方法的参数数据类型。
为什么要使用泛型呢?来看一下下面这个例子,我们知道一个集合可以存储不同数据类型的数据,实例化一个 ArrayList 集合对象,并向该集合中保存两个数据,int 类型的 1 和 String 类型的 “Hello”
public class Test{
public static void main(String[] args){
ArrayList list = new ArrayList();
list.add(2);
list.add("Hello");
for(int i = 0; i , list.size(); i++){
int num = (int) list.get(i);
System.out.println(num+1);
}
}
}
尝试运行会报错,报错原因在于无法将 String 类型的 “Hello” 转为 int 类型,这就是没有设置泛型所带来的的数据不安全问题。因为 list 对数据类型没有要求,任意数据类型都能存入,集合内部是以 Object 类型来保存各种数据的,也是多态的一种体现。存入没有问题,但是在取数据的时候就会出问题,因为不同的数据类型之间往往不能进行类型转换,除非两个数据有继承关系,或者具有接口实现的关系,否则强制进行转换就会抛出异常。
使用泛型就可以避免数据不安全的隐患,接口是支持泛型的。所以我们在实例化 ArrayList 对象的时候就指定泛型为 Integer,这样就限制了可以存入集合的数据,除了 Integer 类型以外的数据类型无法存入集合,当然指定数据类型的子类是可以存入的,这样就保证了集合中数据类型的统一性。在取数据的时候不会抛出数据类型转换类型失败打一场,同时在指定泛型后,集合内部就会以指定的数据类型来保存所有数据,取数据时也就省去了强制类型转换的步骤。
public class Test {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
for(int i = 0; i < list.size(); i++) {
int num = list.get(i).intValue();
System.out.println(num+1);
}
}
}
运行结果:
2
3
泛型的应用
我们除了可以在实例化集合时指定泛型外,自定义的类也可以添加泛型,基本语法如下:
访问权限修饰符 class 类名<泛型标识 1,泛型标识 2..>{
访问权限修饰符 泛型标识 属性名;
访问权限修饰符 泛型标识 方法名(泛型标识 参数名...){}
}
下面我们自己定义一个 Time 类:
class Time<T>{
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
public class TestTime {
public static void main(String[] args) {
Time<Integer> time1 = new Time<Integer>();
time1.setValue(10);
System.out.println("现在的时间是:"+time1.getValue());
Time<String> time2 = new Time<String>();
time2.setValue("十点整");
System.out.println("现在的时间是:"+time2.getValue());
}
}
运行结果:
现在的时间是:10
现在的时间是:十点整
在定义一个类时可以同时指定多个泛型标识:
class Time2<H,M,S>{
private H hour;
private M minute;
private S second;
public H getHour() {
return hour;
}
public void setHour(H hour) {
this.hour = hour;
}
public void setMinute(M minute) {
this.minute = minute;
}
public M getMinute() {
return minute;
}
public void setSecond(S second) {
this.second = second;
}
public S getSecond() {
return second;
}
}
public class TestTime2 {
public static void main(String[] args) {
Time2<String,Integer,Float> time = new Time2<String,Integer,Float>();
time.setHour("十点");
time.setMinute(10);
time.setSecond(10.0f);
System.out.println("现在时间是:"+time.getHour()+":"+time.getMinute()+":"+time.getSecond());
}
}
运行结果:
现在的时间是:十点:10:10.0
泛型通配符
如果我们定义一个参数为 ArrayList 类型的方法时,希望该方法即可接受泛型为 String 的集合参数,也可以接受泛型为 Integer 的集合参数,那应该怎么处理呢?可以用多态的思想来定义该方法,即使用 Object 来定义参数泛型。
public class Test{
public static void main(String[] args){
ArrayList<String> list1 = new ArrayList<String>();
ArrayList<Integer> list2 = new ArrayList<Integer>();
test(list1);
test(list2);
}
public static void test(ArrayList<Object> list){
System.out.println(list);
}
}
这样定义方法,在调用 test() 方法时还是会报错。list1 和 list2 无法匹配 test() 方法的参数类型,String 和 Integer 在泛型引用中不能转换为 Object。所以 test() 方法的参数泛型不能设置为 Object,如何解决?这个问题可以通过泛型通配符来处理,用 ? 表示当前未知的泛型类型。
public class TestWhy {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<String>();
ArrayList<Integer> list2 = new ArrayList<Integer>();
test(list1);
test(list2);
}
public static void test(ArrayList<?> list) {
System.out.println(list);
}
}
这样写就不会报错ArrayList<?> 表示可以使用任意的泛型类型对象,这样 test() 方法就有了通用性
泛型上限和下限
我们在使用泛型时,往往数据类型会有限制,只能使用一种具体的数据类型。如果希望再次基础上进行适量的扩容,可以通过泛型上限和下限来完成。泛型上限表示实例化时的具体数据类型,可以是上限类型的子类或者是上限类型本身,用 extends 关键字来修饰。泛型下限表示实例化时的具体数据类型可以是下限类型的父类或者是下限类型本身,用 super 关键字来修饰,基本语法如下:
- 泛型上限:类名<泛型标识 extends 上限类名>
- 泛型下限:类名<泛型标识 super 下限类名>
具体实现代码如下:
public class TestES {
public static void main(String[] args) {
test(new Time<Integer>());
test(new Time<String>());
test2(new Time<Integer>());
test2(new Time<String>());
test3(new Time<Integer>());
test3(new Time<String>());
}
/*
* 参数的泛型只能是 Number 或者其子类,即 Number,Byte,Short,Long
* Integer,Float,Double
* */
public static void test(Time<? extends Number> time) {
}
/*
* 参数的泛型只能是 String 或者其父类,即 String 和 Object
* */
public static void test2(Time<? super String> time) {
}
/*
* 参数的泛型只能是 Object 或者其子类
*
*/
public static void test3(Time<? extends Object> time) {
}
}
这里会有两个报错的地方,看下图解释:
泛型接口
我们在定义类时可以添加泛型,在定义接口时也可以添加泛型。声明泛型接口的语法和声明泛型类很相似,在接口名后加上即可,基本语法:访问权限修饰符 interface 接口名<泛型标识>,集体实现代码如下:
public interface MyInterface<T>{
public T getValue();
}
实现泛型接口有两种方式,一种是实现类在定义时继续使用泛型标识,另一种是实现类在定义时直接给出具体的数据类型,实现代码如下:
class MyInterfaceImpl<T> implements MyInterface<T>{
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
public MyInterfaceImpl(T obj) {
this.obj = obj;
}
@Override
public T getValue() {
return obj;
}
}
class MyInterfaceImpl2 implements MyInterface<String>{
private String obj;
public String getObj() {
return obj;
}
public void setObj(String obj) {
this.obj = obj;
}
public MyInterfaceImpl2(String obj) {
this.obj = obj;
}
@Override
public String getValue() {
return obj;
}
}
public class Test {
public static void main(String[] args) {
MyInterface<String> myInterface = new MyInterfaceImpl<String>("接口1");
System.out.println(myInterface.getValue());
MyInterface myInterface2 = new MyInterfaceImpl2("接口2");
System.out.println(myInterface2.getValue());
}
}
运行结果:
接口1
接口2
综合练习
我们一起来做一个简单的用户管理系统,要求使用面向对象的编程思想,同时用集合来存储数据。
public class User {
private String name;
private int age;
private String state;
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;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public User(String name, int age, String state) {
this.name = name;
this.age = age;
this.state = state;
}
}
public class UsersDemo {
private static List<User> userList;
static {
userList = new ArrayList<User>();
userList.add(new User("张三",22,"正常"));
userList.add(new User("李四",23,"正常"));
userList.add(new User("王五",20,"正常"));
userList.add(new User("小明",18,"正常"));
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int num = 0;
do {
System.out.println("欢迎使用用户管理系统");
System.out.println("1.查询用户");
System.out.println("2.添加用户");
System.out.println("3.删除用户");
System.out.println("4.账号冻结");
System.out.println("5.账号解封");
System.out.println("6.退出系统");
System.out.print("请选择:");
num = scanner.nextInt();
switch(num) {
case 1:
System.out.println("------查询用户------");
System.out.println("编号\t\t名称\t\t年龄\t\t状态");
for(int i = 0; i < userList.size(); i++) {
User user = userList.get(i);
System.out.println((i+1)+"\t\t"+user.getName()+"\t\t"+user.getAge()+"\t\t"+user.getState());
}
System.out.print("输入 0 返回:");
num = scanner.nextInt();
break;
case 2:
System.out.println("------添加用户------");
System.out.print("请输入用户名称:");
String name = scanner.next();
boolean flag = false;
for(int i = 0; i < userList.size(); i++) {
User user = userList.get(i);
if(user.getName().equals(name)) {
flag = true;
System.out.println("该用户已存在!");
break;
}
}
if(!flag) {
System.out.print("请输入年龄:");
int age = scanner.nextInt();
userList.add(new User(name,age,"正常"));
System.out.println("添加成功!");
}
System.out.print("输入 0 返回:");
num = scanner.nextInt();
break;
case 3:
System.out.println("------删除用户------");
System.out.print("请输入用户名称:");
name = scanner.next();
boolean flag2 = false;
for(int i = 0; i < userList.size(); i++) {
User user = userList.get(i);
if(user.getName().equals(name)) {
flag2 = true;
userList.remove(user);
}
}
if(!flag2) {
System.out.println(name+"用户不存在!");
}else {
System.out.println(name+"删除成功!");
}
System.out.print("输入 0 返回:");
num = scanner.nextInt();
break;
case 4:
System.out.println("------账号冻结------");
System.out.print("请输入用户名称:");
name = scanner.next();
boolean flag3 = false;
for(int i = 0; i < userList.size(); i++) {
User user = userList.get(i);
if(user.getName().equals(name)) {
flag3 = true;
if(user.getState().equals("冻结")) {
System.out.println(name+"已冻结");
}else {
user.setState("冻结");
System.out.println(name+"冻结成功!");
}
break;
}
}
if(!flag3) {
System.out.println(name+"用户不存在!");
}
System.out.print("输入 0 返回:");
num = scanner.nextInt();
break;
case 5:
System.out.println("------账号解封------");
System.out.print("请输入用户名称:");
name = scanner.next();
boolean flag4 = false;
for(int i = 0; i < userList.size(); i++) {
User user = userList.get(i);
if(user.getName().equals(name)) {
flag4 = true;
if(user.getState().equals("正常")) {
System.out.println(name+"状态正常");
}else {
user.setState("正常");
System.out.println(name+"解封成功");
}
break;
}
}
if(!flag4) {
System.out.println(name+"用户不存在!");
}
System.out.print("输入 0 返回:");
num = scanner.nextInt();
break;
case 6:
System.out.println("感谢使用用户管理系统!");
return;
}
}while(num == 0);
}
}
运行结果: