Java泛型01

1.泛型的理解和好处

看一个需求:

  1. 请编写程序,在ArrayList中添加三个Dog对象
  2. 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循环输出的时候向下转型时就会抛出异常:类型转换错误

使用传统方法问题的分析:

  1. 不能对加入到集合ArrayList中的数据进行约束(不安全)
  2. 遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响

使用泛型来解决问题:

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;
}
}

泛型的好处:

  1. 编译时,检查添加元素的类型,提高了安全型

  2. 减少了类型转换的次数,提高效率

    如上面例子所示:不使用泛型的时候,Dog对象放到ArrayList里会先转成Object类型,在取出的时候还要再转换成Dog类型(Dog--加入-->Object--取出-->Dog)

    使用了泛型,则放入和取出时都不需要类型转换,提高效率(Dog-->-Dog-->Dog)

  3. 不再提示编译警告

    不添加@SuppressWarnings("all")编译器也不再警告

2.泛型介绍

泛型是一种可以表示数据类型的 数据类型

如下图:public class ArrayList<E>{} E 称为泛型


泛(广泛)型(类型)===>integer,String,Dog,……

  1. 泛型又称参数化类型,是jdk5.0出现的新特性,解决数据类型的安全性问题
  2. 在类声明或者实例化时只要指定好需要的具体类型即可
  3. Java泛型可以保证如果程序在编译时没有发出警告,运行就不会产生ClassCastException异常。同时,代码更加简洁、健壮
  4. 泛型的作用是:可以在类声明时 通过一个标识 表示类中的某个属性,或者是某个方法的返回值的类型,或者是参数类型

例子:

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泛型使用举例

例子:泛型使用举例:

练习:

  1. 创建三个学生对象
  2. 学生对象放入到HashSet中使用
  3. 放入到HashMap中,要求Key是String name ,Value就是学生对象
  4. 使用两种方法遍历

练习:

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泛型使用的注意事项和细节

  1. interface List<T>{} ,public class HashSet<E>{}..等等

    说明:T,E只能是引用类型

    看看下面语句是否正确?

    List<Integer> list = new ArrayList<Integer>();//正确

    List<int> list2 = new ArrayList<int>();//错误

  2. 在指定泛型具体类型后,可以传入该类型或者其子类类型

  3. 泛型使用形式

    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类

  1. 该类包括:private成员变量name,sal,birthday,其中birthday为MyDate类的对象;

  2. 为每一个属性定义getter、setter方法;

  3. 重写toString方法输出name,sal,birthday;

  4. MyDate类包括:private成员变量year,month,day。并为为每一个属性定义getter、setter方法;

  5. 创建该类的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();
}
}

day28--Java泛型01的更多相关文章

  1. Java泛型解析(01):认识泛型

    Java泛型解析(01):认识泛型 What      Java从1.0版本号到如今的8.中间Java5中发生了一个非常重要的变化,那就是泛型机制的引入.Java5引入了泛型,主要还是为了满足在199 ...

  2. java 泛型 -- 泛型类,泛型接口,泛型方法

    泛型T泛型的许多最佳例子都来自集合框架,因为泛型让您在保存在集合中的元素上指定类型约束.在定义泛型类或声明泛型类的变量时,使用尖括号来指定形式类型参数.形式类型参数与实际类型参数之间的关系类似于形式方 ...

  3. Java泛型解析(03):虚拟机运行泛型代码

    Java泛型解析(03):虚拟机运行泛型代码      Java虚拟机是不存在泛型类型对象的,全部的对象都属于普通类,甚至在泛型实现的早起版本号中,可以将使用泛型的程序编译为在1.0虚拟机上可以执行的 ...

  4. [Java 教程 01] Hello,Java!

    前言 从事编程已经有一段时间了,突然发现,Java作为我的第一编程语言,自己似乎对她并有一个系统的思想.当下Java依旧保持着超高的热度,新特性也不断出现,从当初学习的java6版本到最近刚出的jav ...

  5. Java泛型学习一

    Java泛型 所谓泛型,就是变量类型的参数化.泛型是java1.5中引入的一个重要特征,通过引入泛型,可以使编译时类型安全,运行时更少抛出ClassCastException的可能.一提到参数化,最熟 ...

  6. Java泛型解析(04):约束和局限性

    Java泛型解析(04):约束和局限性           前两节.认识和学习了泛型的限定以及通配符.刚開始学习的人可能须要一些时间去体会到泛型程序设计的优点和力量,特别是想成为库程序猿的同学就须要下 ...

  7. Java泛型解析(02):通配符限定

    Java泛型解析(02):通配符限定      考虑一个这种场景.计算数组中的最大元素. [code01] public class ArrayUtil { public static <T&g ...

  8. 在Java泛型

    1,泛型的定义以及存在意义 泛型,即"参数化类型".就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传 ...

  9. Java泛型的历史

    为什么Java泛型会有当前的缺陷? 之前的章节里已经说明了Java泛型擦除会导致的问题,C++和C#的泛型都是在运行时存在的,难道Java天然不支持“真正的泛型”吗? 事实上,在Java1.5在200 ...

  10. 浅析Java 泛型

    泛型是JavaSE5引入的一个新概念,但是这个概念在编程语言中却是很普遍的一个概念.下面,根据以下内容,我们总结下在Java中使用泛型. 泛型使用的意义 什么是泛型 泛型类 泛型方法 泛型接口 泛型擦 ...

随机推荐

  1. 多进程|基于非阻塞调用的轮询检测方案|进程等待|重新理解挂起|Linux OS

    说在前面 今天给大家带来操作系统中进程等待的概念,我们学习的操作系统是Linux操作系统. 我们今天主要的目标就是认识wait和waitpid这两个系统调用. 前言 那么这里博主先安利一下一些干货满满 ...

  2. JuiceFS v1.0 正式发布,首个面向生产环境的 LTS 版本

    今天,JuiceFS v1.0 发布了 经过了 18 个月的持续迭代和大量生产环境的广泛验证,此版本将成为第一个被长期维护的稳定版(LTS).同时,该版本提供完整的向前兼容,所有用户可以直接升级. J ...

  3. Zookeeper-ZKFC的原理和功能

    一.前言 HADOOP2 HA架构引入了ZKFC.Journalnode组件,本篇文章主要介绍ZKFC的功能和原理.HA架构支持两种切换方式: 手动切换:  通过命令实现主备之间的切换,可以用HDFS ...

  4. zookeeper源码(08)请求处理及数据读写流程

    ServerCnxnFactory 用于接收客户端连接.管理客户端session.处理客户端请求. ServerCnxn抽象类 代表一个客户端连接对象: 从网络读写数据 数据编解码 将请求转发给上层组 ...

  5. Windows也能拥有好用的命令行吗?Powershell+Terminal折腾记录(v1.0版本)

    PS:本文写于2021年,现在已经是2024年,有了很多新变化,我在接下来的文章里会继续更新. 前言 Windows一向以图形化操作入门容易著称,所以对于命令行的支持一直为人所诟病,比起Linux或者 ...

  6. Laravel入坑指南(9)——数据迁移与填充

    当我们开发完成一个(小)项目,发布到线上时,我们需要将本地数据库迁移到服务器上,并且填充初始化数据.而Laravel框架规定了一套完善的数据迁移与填充机制. 在官网中分别介绍了以下四个命令: php ...

  7. ORACLE SEQUENCE 详解

    1.    About Sequences(关于序列) 序列是数据库对象一种.多个用户可以通过序列生成连续的数字以此来实现主键字段的自动.唯一增长,并且一个序列可为多列.多表同时使用. 序列消除了串行 ...

  8. Jsp+Servlet实现文件上传下载(三)--删除上传文件

    接着上一篇讲: Jsp+Servlet实现文件上传下载(二)--文件列表展示点击打开链接 本章来实现一下删除已上传文件,同时优化了一下第一章中的代码. 废话少说,上代码 --------------- ...

  9. 对yuv存储格式中的yuv420p和yuv420sp的理解

    一.对yuv的认识 yuv是一种颜色编码系统,它将图像的亮度和色度分离开来.y表示亮度,即黑白信息:uv表示色度,即颜色信息.yuv常用于视频压缩和传输中,因为它可以更有效地表示人眼对亮度和色度的敏感 ...

  10. git bash 的一些使用

    一般使用git bash需要的命令 先打开git bash: git init 可以初始化一个本地的仓库 git status 查看仓库信息 mkdir test 创建一个test的文件夹 cd te ...