草庐IT

day28--Java泛型01

liyuelian 2023-04-16 原文

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 - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  2. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  3. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  4. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  5. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  6. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

  7. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  8. java - 为什么 ruby​​ modulo 与 java/other lang 不同? - 2

    我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.

  9. java - Ruby 相当于 Java 的 Collections.unmodifiableList 和 Collections.unmodifiableMap - 2

    Java的Collections.unmodifiableList和Collections.unmodifiableMap在Ruby标准API中是否有等价物? 最佳答案 使用freeze应用程序接口(interface):Preventsfurthermodificationstoobj.ARuntimeErrorwillberaisedifmodificationisattempted.Thereisnowaytounfreezeafrozenobject.SeealsoObject#frozen?.Thismethodretur

  10. java - Java 的 StringReader 的 Ruby 等价物是什么? - 2

    在Java中,可以像这样从一个字符串创建一个IO流:Readerr=newStringReader("mytext");我希望能够在Ruby中做同样的事情,这样我就可以获取一个字符串并将其视为一个IO流。 最佳答案 r=StringIO.new("mytext")和here'sthedocumentation. 关于java-Java的StringReader的Ruby等价物是什么?,我们在StackOverflow上找到一个类似的问题: https://st

随机推荐