JavaSE - 枚举与泛型

JavaSE - 枚举与泛型

本节学习目标:

  • 了解枚举类型的概念;
  • 了解并掌握枚举类型的使用方式;
  • 了解Enum类及其方法;
  • 了解泛型的概念;
  • 了解并掌握泛型类的使用方式;

1. 枚举类型

使用枚举类型(Enumeration)可以取代以往定义常量的方式,同时枚举类型还赋予程序在编译时进行检查的功能。

1.1 枚举类型的定义

早期设置常量时,通常将常量放置在接口中,这样在程序中可以直接使用,同时该常量不能被修改

public interface Color {
     
    String RED = "红色";
    String GREEN = "绿色";
    String BLUE = "蓝色";
}

JDK1.5之后Java提供了枚举类型,使用枚举类型可以更好的定义常量。枚举类型中的常量使用逗号分隔,并在最后一个常量后使用分号结尾。:

public enum Color {
     
    RED, GREEN, BLUE;
}

Java使用enum关键字来定义枚举类,需要在程序中使用常量时可以使用枚举类名.常量名来获取。

枚举类同样可以以内部类形式被定义在一个类的内部

public class Car {
     
    enum Type {
     
        CAR, TRUCK, BUS;
    }
}

常量可以看做是枚举类的一个对象,枚举类中至少需要定义一个常量,并且只能在开头定义。

1.2 枚举类型的成员

1. 成员变量与静态常量

枚举类中允许定义成员变量静态常量

public enum Signal {
     
    
    OK, FAIL;
    
    private String message;
    public static final String DESC = "这是一个枚举类";
}

2. 构造方法

枚举类中允许定义构造方法,但构造方法必须被private修饰符修饰,因为枚举类的常量数量是固定的,不允许再创建常量:

public enum Signal {
     
    
    OK, FAIL;
    
    private String message;
    private Signal() {
     
        
    }
    Signal(String message) {
     
        this.message = message;
    }
}

由于枚举类中的构造方法必须被private修饰符修饰,因此定义构造方法时可以不写private关键字,默认权限就为private

枚举类一般不写无参构造方法,枚举类的常量直接在枚举类中初始化,这样这些常量可以直接使用:

public enum Signal {
     
    
    OK("成功"), FAIL("失败");
    
    private String message;
    Signal(String message) {
     
        this.message = message;
    }
}

按照有参构造方法的参数列表,在常量后加括号,在括号里传入构造方法的参数

3. 成员方法与静态方法

枚举类中允许定义成员方法静态方法

public enum Signal {
     

    OK("成功"), FAIL("失败");

    private String message;
    Signal(String message) {
     
        this.message = message;
    }
    public String getMessage() {
     
        return message;
    }
    public static void foo() {
     
        System.out.println("调用了静态方法");
    }
}

对于被private修饰的成员变量,为了实现封装性可以为这个成员变量定义getter和setter方法。用来向外部提供访问。但是枚举类的常量一般是不能更改的,所以setter方法可以不写。

例子中的成员变量可以以Signal.OK.getMessage()形式访问。

1.3 Enum 类

Enum类位于java.lang包下,它是所有枚举类(使用enum关键字定义的枚举类)的父类。

它有两个成员变量:

变量名 类型 说明
name String 枚举类中每一个变量的名称
ordinal int 枚举类中每一个变量的序数

比如章节1.2的例子中的常量OK,它的名称是OK,序数为0;常量FAIL的名称为FAIL,序数为1

Enum类提供了很多常用方法:

方法名 返回值类型 功能
name() String 返回枚举类中指定常量的名称
ordinal() int 返回枚举类中指定常量的序数
toString() String 继承于Object类,返回枚举类中指定常量字符串形式,可重写
valueOf(Class enumType, String name) > T 将字符串name解析为指定枚举类enumType中的常量T
compareTo(E o) int 返回枚举类的两个常量的序数差,如果这两个常量不是来自同一个枚举类,将抛出ClassCastException异常
equals(Object other) boolean 继承于Object类,将枚举类中指定常量与对象other进行比较,可重写

枚举类本身提供的方法:

方法名 返回值类型 功能
values() > T[] 返回当前枚举类中所有常量,使用数组表示
valueOf(String name) > T 将字符串name解析为当前枚举类中的常量T

编写代码进行测试:

import java.util.Arrays;
public class Test {
     
    public static void main(String[] args) {
     
        System.out.println(Signal.OK.name());
        System.out.println(Signal.OK.ordinal());
        System.out.println(Signal.OK.toString());
        System.out.println(Enum.valueOf(Signal.class, "OK").name());
        System.out.println(Signal.OK.compareTo(Signal.FAIL));
        System.out.println(Signal.OK.equals(Signal.FAIL));
        System.out.println(Signal.OK.equals(Signal.OK));
        System.out.println(Arrays.toString(Signal.values()));
        System.out.println(Signal.valueOf("OK"));
    }
}

运行结果:

OK
0
OK
OK
-1
false
true
[OK, FAIL]
OK

1.4 枚举类型的优点

  • 类型安全;
  • 数据定义紧凑有效;
  • 可以和程序其他部分完美交互;
  • 运行效率高;

2. 泛型

Java泛型(Generics)是JDK1.5中引入的新特性,泛型提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型。

在没有出现泛型之前,Java提供了对Object的泛指引用,原理是对继承的向上转型向下转型操作。但是向下转型操作由于使用了强制类型转换,容易出现异常,存在安全隐患。

编写代码演示使用Object类的泛指引用:

public class Test {
     
    private Object obj;
    public Object getObj() {
     
        return obj;
    }
    public void setObj(Object obj) {
     
        this.obj = obj;
    }
    public static void main(String[] args) {
     
        Test test = new Test();
        test.setObj(new Float(457.484167F)); // Float向上转型为Object
        System.out.println(test.obj);
        Integer i = (Integer) test.getObj(); // Object向下转型为Integer
        System.out.println(i);
    }
}

运行结果:

457.48416
Exception in thread "main" java.lang.ClassCastException: java.lang.Float cannot be cast to java.lang.Integer
	at Test.main(Test.java:13)

可以看到进行向下转型操作是出现了异常,而代码通过了编译。为了解决这个问题,Java引入了泛型机制。

2.1 泛型的声明

泛型又叫参数化类型(Parameterized Class),在类名后或者方法的返回值前可以使用尖括号(<>)进行泛型的声明,格式为:

<泛型标记符>

如果要声明多个泛型标记符,使用逗号隔开:

<泛型标记符1, 泛型标记符2>

泛型标记符和标识符命名规则相同,实际开发中一般以单个大写字母表示。Java中常用的泛型标记符:

  • E:一般在集合类使用,代表集合的元素类型(Element);
  • T:Java(Type);
  • K:键值对的(Key);
  • V:键值对的(Value);
  • N数值类型(Number);
  • ?(特殊):类型通配符,表示不确定的Java类型。

泛型标记符的实质是一个参数,这个参数代表泛型的类型,因此存在作用范围。在类名后声明作用范围是整个类,在方法的返回值名前声明作用范围仅限方法内使用。

泛型的类型只能是引用数据类型,不能是基本数据类型

2.2 泛型的用法

泛型可以在中定义,也可以在方法中定义。泛型不可以直接在类中或方法中使用new关键字进行实例化(类似T t = new T();),因为它们还不是某个具体类型

1. 泛型类

在类名后声明泛型,使用这个类时必须提供具体类型:

public class GenericsClass<T> {
     
    private T obj;
    public T getObj() {
     
        return obj;
    }
    public void setObj(T obj) {
     
        this.obj = obj;
    }
    public static void main(String[] args) {
     
        GenericsClass<Float> f = new GenericsClass<>();
        f.setObj(new Float(4857.46));
        System.out.println(f.getObj());
        Integer i = (Integer) f.getObj();
        System.out.println(i);
    }
}

尝试编译,编译失败:

JavaSE - 枚举与泛型_第1张图片

使用泛型进行非法强制类型转换会直接报错,无法进行编译,所以使用泛型更加安全。

一旦指定泛型为某个具体类型后,泛型的对象等引用的类型就是指定的具体类型

2. 泛型方法

在方法的返回值前声明泛型,使用这个方法时必须提供具体类型:

public class Test {
     
    public static <T> void getTClass(T t) {
     
        System.out.println("传入的参数的类型为" + t.getClass().getName());
    }
    public static void main(String[] args) {
     
        getTClass(457);
        getTClass("asd");
        getTClass(999.99);
    }
}

运行结果:

传入的参数的类型为java.lang.Integer
传入的参数的类型为java.lang.String
传入的参数的类型为java.lang.Double

3. 声明多个泛型

可以声明多个泛型,编写代码进行测试:

public class GenericEntry<K, V> {
     
    private K key;
    private V value;
    public GenericEntry(K key, V value) {
     
        this.key = key;
        this.value = value;
    }
    public K getKey() {
     
        return key;
    }
    public V getValue() {
     
        return value;
    }
    public static void main(String[] args) {
     
        GenericEntry<Integer, String> entry = new GenericEntry<>(1, "示例代码");
        System.out.println(entry.getValue());
    }
}
// 运行结果
// 示例代码

2.3 泛型的限制

可以通过对泛型的限制进一步限制类型。使用extends关键字和super关键字来限制泛型的类型:

1. 向上限定

使用extends关键字进行向上限定,限制泛型只能使用指定类型及其子类

<泛型标识符 extends Object>

编写代码进行测试:

public class GenericExtends<T extends Number> {
     
    private T number;
    public T getNumber() {
     
        return number;
    }
    public void setNumber(T number) {
     
        this.number = number;
    }

    public static void main(String[] args) {
     
        GenericExtends<String> obj = new GenericExtends<>(); // 尝试将泛型设置为String类型
    }
}

尝试编译,编译失败:

JavaSE - 枚举与泛型_第2张图片

2. 向下限定与类型通配符

Java的泛型机制提供了类型通配符(?),表示未知类型的泛型,需要使用extends关键字或super关键字加以限制。此机制只能在指定泛型的具体类型时(即使用泛型类泛型方法时)使用。

使用super关键字进行向下限定,需要配合使用类型通配符(?),限制泛型只能使用指定类型及其父类

<? super Object>

编写代码进行测试:

public class GenericSuper<T> {
     
    private T obj;
    public T getObj() {
     
        return obj;
    }
    public void setObj(T obj) {
     
        this.obj = obj;
    }
    public static void main(String[] args) {
     
        GenericSuper<? super Integer> obj = new GenericSuper<>();
        obj.setObj(57.3); // 尝试将泛型设置为其他类型
        System.out.println(obj.getObj());
    }
}

尝试编译,编译失败:

JavaSE - 枚举与泛型_第3张图片

你可能感兴趣的:(我的Java基础学习之路,java,开发语言,后端,javase)