枚举(enum)是Java中的一种特殊类型,用于定义一组常量。枚举类型可以提高代码的可读性和可维护性,使得常量的使用更加清晰和安全。以下是关于枚举的详细介绍:
ENUM的源码
package java.lang;
import java.io.Serializable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
/*** This is the common base class of all Java language enumeration types.** More information about enums, including descriptions of the* implicitly declared methods synthesized by the compiler, can be* found in section 8.9 of* <cite>The Java™ Language Specification</cite>.** <p> Note that when using an enumeration type as the type of a set* or as the type of the keys in a map, specialized and efficient* {@linkplain java.util.EnumSet set} and {@linkplain* java.util.EnumMap map} implementations are available.** @param <E> The enum type subclass* @author Josh Bloch* @author Neal Gafter* @see Class#getEnumConstants()* @see java.util.EnumSet* @see java.util.EnumMap* @since 1.5*/
public abstract class Enum<E extends Enum<E>>implements Comparable<E>, Serializable {/*** The name of this enum constant, as declared in the enum declaration.* Most programmers should use the {@link #toString} method rather than* accessing this field.*/private final String name;
/*** Returns the name of this enum constant, exactly as declared in its* enum declaration.** <b>Most programmers should use the {@link #toString} method in* preference to this one, as the toString method may return* a more user-friendly name.</b> This method is designed primarily for* use in specialized situations where correctness depends on getting the* exact name, which will not vary from release to release.** @return the name of this enum constant*/public final String name() {return name;}
/*** The ordinal of this enumeration constant (its position* in the enum declaration, where the initial constant is assigned* an ordinal of zero).** Most programmers will have no use for this field. It is designed* for use by sophisticated enum-based data structures, such as* {@link java.util.EnumSet} and {@link java.util.EnumMap}.*/private final int ordinal;
/*** Returns the ordinal of this enumeration constant (its position* in its enum declaration, where the initial constant is assigned* an ordinal of zero).** Most programmers will have no use for this method. It is* designed for use by sophisticated enum-based data structures, such* as {@link java.util.EnumSet} and {@link java.util.EnumMap}.** @return the ordinal of this enumeration constant*/public final int ordinal() {return ordinal;}
/*** Sole constructor. Programmers cannot invoke this constructor.* It is for use by code emitted by the compiler in response to* enum type declarations.** @param name - The name of this enum constant, which is the identifier* used to declare it.* @param ordinal - The ordinal of this enumeration constant (its position* in the enum declaration, where the initial constant is assigned* an ordinal of zero).*/protected Enum(String name, int ordinal) {this.name = name;this.ordinal = ordinal;}
/*** Returns the name of this enum constant, as contained in the* declaration. This method may be overridden, though it typically* isn't necessary or desirable. An enum type should override this* method when a more "programmer-friendly" string form exists.** @return the name of this enum constant*/public String toString() {return name;}
/*** Returns true if the specified object is equal to this* enum constant.** @param other the object to be compared for equality with this object.* @return true if the specified object is equal to this* enum constant.*/public final boolean equals(Object other) {return this==other;}
/*** Returns a hash code for this enum constant.** @return a hash code for this enum constant.*/public final int hashCode() {return super.hashCode();}
/*** Throws CloneNotSupportedException. This guarantees that enums* are never cloned, which is necessary to preserve their "singleton"* status.** @return (never returns)*/protected final Object clone() throws CloneNotSupportedException {throw new CloneNotSupportedException();}
/*** Compares this enum with the specified object for order. Returns a* negative integer, zero, or a positive integer as this object is less* than, equal to, or greater than the specified object.** Enum constants are only comparable to other enum constants of the* same enum type. The natural order implemented by this* method is the order in which the constants are declared.*/public final int compareTo(E o) {Enum<?> other = (Enum<?>)o;Enum<E> self = this;if (self.getClass() != other.getClass() && // optimizationself.getDeclaringClass() != other.getDeclaringClass())throw new ClassCastException();return self.ordinal - other.ordinal;}
/*** Returns the Class object corresponding to this enum constant's* enum type. Two enum constants e1 and e2 are of the* same enum type if and only if* e1.getDeclaringClass() == e2.getDeclaringClass().* (The value returned by this method may differ from the one returned* by the {@link Object#getClass} method for enum constants with* constant-specific class bodies.)** @return the Class object corresponding to this enum constant's* enum type*/@SuppressWarnings("unchecked")public final Class<E> getDeclaringClass() {Class<?> clazz = getClass();Class<?> zuper = clazz.getSuperclass();return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;}
/*** Returns the enum constant of the specified enum type with the* specified name. The name must match exactly an identifier used* to declare an enum constant in this type. (Extraneous whitespace* characters are not permitted.)** <p>Note that for a particular enum type {@code T}, the* implicitly declared {@code public static T valueOf(String)}* method on that enum may be used instead of this method to map* from a name to the corresponding enum constant. All the* constants of an enum type can be obtained by calling the* implicit {@code public static T[] values()} method of that* type.** @param <T> The enum type whose constant is to be returned* @param enumType the {@code Class} object of the enum type from which* to return a constant* @param name the name of the constant to return* @return the enum constant of the specified enum type with the* specified name* @throws IllegalArgumentException if the specified enum type has* no constant with the specified name, or the specified* class object does not represent an enum type* @throws NullPointerException if {@code enumType} or {@code name}* is null* @since 1.5*/public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name) {T result = enumType.enumConstantDirectory().get(name);if (result != null)return result;if (name == null)throw new NullPointerException("Name is null");throw new IllegalArgumentException("No enum constant " + enumType.getCanonicalName() + "." + name);}
/*** enum classes cannot have finalize methods.*/protected final void finalize() { }
/*** prevent default deserialization*/private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException {throw new InvalidObjectException("can't deserialize enum");}
private void readObjectNoData() throws ObjectStreamException {throw new InvalidObjectException("can't deserialize enum");}
}
1. 基本概念
-
定义:枚举是一个特殊的类,它包含一组常量(枚举值)。在Java中,使用
enum关键字定义枚举类型。 -
目的:枚举类型可以使代码更具可读性,确保变量仅能取特定值,减少出错的可能性。
2. 枚举的基本语法
enum Season {SPRING, SUMMER, AUTUMN, WINTER;
}
上面的定义创建了一个名为Season的枚举类型,包含四个枚举常量:SPRING、SUMMER、AUTUMN和WINTER。
3. 带参数的枚举
枚举还可以定义属性和构造方法。每个枚举常量可以具有自己的属性值。
enum Season {SPRING("春天", "我喜欢春天"),SUMMER("夏天", "炎热的夏天"),AUTUMN("秋天", "我言秋日胜春朝"),WINTER("冬天", "最喜欢冬天");
private final String name;private final String description;
// 构造方法private Season(String name, String description) {this.name = name;this.description = description;}
public String getName() {return name;}
public String getDescription() {return description;}
@Overridepublic String toString() {return name + ": " + description;}
}
4. 枚举的特性
-
类型安全:枚举提供了类型安全,确保变量仅能取定义的枚举值,避免了使用整数常量时的类型错误。
-
隐式继承:枚举隐式继承自
java.lang.Enum类,无法继承其他类。 -
单例:每个枚举常量都是一个单例,Java保证每个枚举实例只有一个。
-
可以实现接口:枚举可以实现接口,但不能继承类。
5. 使用枚举的优点
-
代码可读性:枚举常量的命名使代码更易理解,减少了使用数字常量可能造成的混淆。
-
限制性:限制变量只能取预定义的值,减少了出错的几率。
-
更强的功能:可以为枚举定义方法和属性,使其更具功能性。
-
内置方法:Java提供了一些内置方法,如
values()和valueOf(),可用于枚举类型的操作。-
values()方法返回枚举类型的所有常量。 -
valueOf(String name)方法返回与给定名称匹配的枚举常量。
-
6. 示例代码
以下是一个完整的枚举示例,包括常量、属性、构造方法和方法:
public class EnumExample {public static void main(String[] args) {for (Season season : Season.values()) {System.out.println(season); // 调用toString()方法}}
}
enum Season {SPRING("春天", "我喜欢春天"),SUMMER("夏天", "炎热的夏天"),AUTUMN("秋天", "我言秋日胜春朝"),WINTER("冬天", "最喜欢冬天");
private final String name;private final String description;
// 构造方法private Season(String name, String description) {this.name = name;this.description = description;}
public String getName() {return name;}
public String getDescription() {return description;}
@Overridepublic String toString() {return name + ": " + description;}
}
7. 枚举与 switch 语句
枚举可以与switch语句一起使用,使条件判断更加简洁明了:
Season season = Season.SPRING;
switch (season) {case SPRING:System.out.println("春天来了!");break;case SUMMER:System.out.println("夏天到了!");break;case AUTUMN:System.out.println("秋天来了!");break;case WINTER:System.out.println("冬天到了!");break;
}
总结
枚举是Java语言中非常有用的特性,能够提高代码的可读性和可维护性,限制变量取值,避免错误。在需要定义一组相关常量时,使用枚举是一个非常好的选择。如果有更多问题或想深入了解的内容,请随时询问!
枚举对象访问示例
在Java中,枚举的对象可以直接通过枚举的名称进行访问。每个枚举常量都是其枚举类型的单例实例,使用枚举名称可以轻松访问这些常量。
假设我们定义了一个名为Season的枚举类型,如下所示:
enum Season {SPRING("春天", "我喜欢春天"),SUMMER("夏天", "炎热的夏天"),AUTUMN("秋天", "我言秋日胜春朝"),WINTER("冬天", "最喜欢冬天");
private final String name;private final String description;
// 构造方法private Season(String name, String description) {this.name = name;this.description = description;}
public String getName() {return name;}
public String getDescription() {return description;}
@Overridepublic String toString() {return name + ": " + description;}
}
直接访问枚举常量
在main方法中,可以直接通过枚举名称来访问枚举常量:
public class EnumExample {public static void main(String[] args) {// 直接通过枚举的名称访问常量Season spring = Season.SPRING;Season summer = Season.SUMMER;// 输出季节的信息System.out.println(spring); // 输出:春天: 我喜欢春天System.out.println(summer); // 输出:夏天: 炎热的夏天// 直接在打印时访问枚举常量System.out.println(Season.AUTUMN); // 输出:秋天: 我言秋日胜春朝System.out.println(Season.WINTER); // 输出:冬天: 最喜欢冬天}
}
关键点
-
直接访问:通过枚举名称(如
Season.SPRING、Season.SUMMER等)可以直接访问每个枚举常量。 -
类型安全:通过使用枚举,编译器能够检查枚举常量的使用,减少了错误的可能性。
-
简洁性:枚举常量的使用使代码更加简洁,易于理解。
小结
枚举类型在Java中是非常强大的特性,可以帮助开发者以更清晰的方式定义常量组,并提供类型安全的使用方式。通过枚举的名称直接访问其常量使得代码更加直观,避免了使用魔法数字或字符串带来的潜在错误。如果你还有其他疑问或需要进一步的信息,请随时问我!
在 Java 中编译:
-
编译:使用
javac命令来编译 Java 源文件(.java),生成字节码文件(.class)。例如:javac MyClass.java
-
反编译:使用
javap命令来查看编译后的.class文件的字节码信息,通常用来分析编译器生成的内容。它不会完全恢复源码,但会显示类的结构、方法签名等。例如:javap MyClass
你可以使用
-c选项查看字节码指令:javap -c MyClass
所以 javac 是编译器,而 javap 是一个基本的反编译工具,用于查看 .class 文件中的信息。
javac:Java 编译器,将 .java 文件编译成 .class 字节码文件。
java:Java 运行时工具,用于运行 .class 文件。
javap:Java 反编译工具,查看 .class 文件的字节码内容。
javadoc:生成 API 文档的工具。
jar:打包工具,用于将 .class 文件等资源打包成 .jar 文件。
枚举类底层源码、
这个代码段展示了一个 Season01 枚举类的字节码结构。让我们逐行注释和解释每个部分的含义:
final class com.enumeration.learn.Season01 extends java.lang.Enum<com.enumeration.learn.Season01> {
-
Season01类被声明为final,表示不能被继承。 -
Season01继承自java.lang.Enum,这是所有 Java 枚举类的超类,使得Season01具备枚举特性。
public static final com.enumeration.learn.Season01 Spring; public static final com.enumeration.learn.Season01 Winter; public static final com.enumeration.learn.Season01 Autumn; public static final com.enumeration.learn.Season01 Summer;
-
这些是
Season01枚举中的四个枚举常量:Spring、Winter、Autumn、Summer。 -
每个枚举常量都是
public static final的,因此它们是常量、不可变的,并且可以通过Season01.Spring直接访问。
public static com.enumeration.learn.Season01[] values();
-
values()是一个自动生成的方法,返回一个Season01枚举数组,其中包含所有定义的枚举常量。 -
这是一个静态方法,通常用于遍历所有枚举实例。
public static com.enumeration.learn.Season01 valueOf(java.lang.String);
-
valueOf(String name)是另一个自动生成的静态方法,返回与传入的字符串名称匹配的Season01枚举常量。 -
如果传入的名称不匹配任何枚举常量,则抛出
IllegalArgumentException。
public java.lang.String getDescription(); public java.lang.String getName();
-
getDescription()和getName()是两个自定义方法,通常用来返回枚举常量的描述信息和名称。 -
返回类型是
String。
public java.lang.String toString();
-
toString()是重写的toString方法,用于返回该枚举常量的字符串表示,通常包括其名称或描述信息。
static {};
-
这是一个静态初始化块,枚举类在加载时会自动初始化所有枚举常量。
以下是 Season01 类的字节码结构的注释版,解释每个字段和方法的作用:
// Season01 类被声明为 final,表示不能被继承
// 它继承自 java.lang.Enum,这是所有 Java 枚举类的超类
final class com.enumeration.learn.Season01 extends java.lang.Enum<com.enumeration.learn.Season01> {
// 定义了四个枚举常量:Spring、Winter、Autumn、Summer// 每个常量都是 public static final,表示可以通过 Season01.Spring 等直接访问public static final com.enumeration.learn.Season01 Spring;public static final com.enumeration.learn.Season01 Winter;public static final com.enumeration.learn.Season01 Autumn;public static final com.enumeration.learn.Season01 Summer;
// values() 方法,自动生成,用于返回包含所有枚举常量的数组// 这是一个静态方法,通常用于遍历所有枚举实例// 这里使用的是权限定名。public static com.enumeration.learn.Season01[] values();
// valueOf(String name) 方法,自动生成,返回与指定名称匹配的 Season01 枚举常量// 如果没有匹配的常量,则抛出 IllegalArgumentExceptionpublic static com.enumeration.learn.Season01 valueOf(java.lang.String);
// getDescription() 方法,自定义,用于返回枚举常量的描述信息public java.lang.String getDescription();
// getName() 方法,自定义,用于返回枚举常量的名称public java.lang.String getName();
// 重写的 toString() 方法,用于返回枚举常量的字符串表示public java.lang.String toString();
// 静态初始化块,枚举类在加载时会自动初始化所有枚举常量static {};
}
这些注释详细说明了 Season01 类的每个字段和方法的作用,使代码更易于理解。
values() 和 valueOf(String name) 是 Java 编译器自动生成 的方法,用于处理枚举常量。它们属于 Java 枚举类型的底层机制,并且在每个枚举类型中自动存在。这两个方法的作用和使用方式如下:
1. values() 方法
-
概述:
values()是编译器为每个枚举类型生成的静态方法。这个方法返回一个数组,其中包含该枚举类型中的所有枚举常量,按照声明的顺序排列。 -
返回值:返回一个数组,数组元素的类型与枚举类相同,例如
Season01[]。 -
典型用途:由于
values()方法提供了所有枚举常量的集合,它在遍历枚举常量时非常有用。例如:for (Season01 season : Season01.values()) {System.out.println(season); }这段代码会依次输出
Season01枚举中的所有常量。 -
示例:
public static com.enumeration.learn.Season01[] values();
对于
Season01类,调用Season01.values()返回[Season01.Spring, Season01.Summer, Season01.Autumn, Season01.Winter]这样的数组。 -
底层实现:在底层,
values()方法返回的是枚举类中的所有实例,它通过反射或特殊的编译器处理来实现。
2. valueOf(String name) 方法
-
概述:
valueOf(String name)方法也是自动生成的,用于返回枚举中指定名称的常量。调用Enum.valueOf(Class<T> enumType, String name)作为基础逻辑。 -
参数:
name参数是一个字符串,必须与枚举常量的名称完全匹配(区分大小写)。 -
返回值:返回与指定名称对应的枚举常量对象。例如,如果传入
"Spring",则返回Season01.Spring。 -
异常处理:如果
name不匹配任何枚举常量,则抛出IllegalArgumentException。如果传入null,则抛出NullPointerException。 -
典型用途:
valueOf(String name)在需要通过字符串匹配来获取枚举常量时很有用。例如,从用户输入的字符串转换成枚举常量:String seasonName = "Spring"; Season01 season = Season01.valueOf(seasonName); System.out.println(season); // 输出:Spring
-
示例:
public static com.enumeration.learn.Season01 valueOf(java.lang.String);
对于
Season01枚举,调用Season01.valueOf("Spring")返回Season01.Spring。 -
底层实现:底层实现通过反射机制,调用
Enum.valueOf来查找匹配的枚举实例。
总结
-
values()和valueOf(String name)的特性:-
这两个方法是由编译器自动生成的,并且存在于所有枚举类型中。
-
这两个方法的作用不同:
values()返回所有枚举常量的数组,valueOf根据名称返回单个枚举常量。 -
valueOf(String name)方法依赖于字符串名称精确匹配,values()方法可以用于循环或枚举遍历。
-
全限定名的意义
在 Java 中,com.enumeration.learn.Season01[] 这样的写法是为了指定 Season01 枚举类的全限定名(fully qualified name)。让我们来详细了解一下这个概念以及为什么在方法声明中使用全限定名。
-
包的概念:
-
Java 中的类和枚举通常会被组织在不同的包(package)中。包是为了避免命名冲突、组织类和接口以及提供访问控制的一种机制。
-
在这个例子中,
com.enumeration.learn是Season01类所在的包名。它指明了Season01类的完整路径。
-
-
避免命名冲突:
-
使用全限定名可以避免不同包中类名相同的情况。例如,如果在另一个包中也有一个名为
Season01的类,使用com.enumeration.learn.Season01可以清楚地指向特定的Season01枚举,而不会引发混淆。 -
如果不使用全限定名,Java 编译器会尝试查找当前包中的
Season01类。如果当前包中没有找到,会去搜索导入的包,这样可能会导致编译错误或使用错误的类。
-
-
清晰性:
-
使用全限定名可以提高代码的可读性,特别是在大型项目中,其他开发人员可以一眼看出
Season01是属于哪个包的类。 -
这对于理解类之间的关系、包的结构和上下文非常有帮助。
-
方法返回类型的声明
在方法声明中使用全限定名的主要原因如下:
-
清晰性与可读性:即使在当前文件中不需要使用全限定名,使用它可以明确表明你所引用的具体类或枚举,尤其是在大型代码库中。
-
上下文明确性:当在同一个文件或类中引用多个不同包中的同名类时,使用全限定名可以确保代码的上下文明确。
直接使用 Season01[]
-
可行性:如果在当前类文件的顶部导入了
com.enumeration.learn.Season01(通过import com.enumeration.learn.Season01;),那么可以直接写Season01[]作为返回类型,这样也是合法的。import com.enumeration.learn.Season01; public static Season01[] values() {// 方法实现 } -
注意:在没有导入声明的情况下,使用
com.enumeration.learn.Season01[]是必要的,以确保 Java 编译器能够正确解析到对应的枚举类型。
总结
-
使用
com.enumeration.learn.Season01[]的目的是为了确保引用的明确性、避免命名冲突和增强代码的可读性。 -
如果导入了相应的包,可以直接使用
Season01[],但在没有导入的情况下,使用全限定名是必须的。
在Java的底层使用权限定名的意义
在 Java 顶层源码中,使用全限定名(fully qualified name)来表示类或枚举类型确实可以避免潜在的命名冲突,特别是在底层代码中。下面是一些关键点,以帮助你进一步理解这个概念:
全限定名的必要性
-
命名冲突的避免:
-
Java 中允许不同的包包含同名的类或接口。如果不使用全限定名,编译器可能会产生错误,或者可能引用错误的类。
-
例如,如果有两个不同的包
com.example.Season和com.anotherexample.Season,如果仅使用Season作为类名引用,编译器就无法确定具体指向哪个类,可能导致命名冲突。
-
-
清晰性与可读性:
-
在复杂的系统或大型项目中,使用全限定名可以使代码更具可读性,让开发者一目了然地了解类的来源和上下文。
-
这有助于其他开发人员在阅读代码时快速识别出类所在的包,从而更好地理解代码结构。
-
-
Java 底层实现:
-
Java 底层代码、标准库以及一些框架在实现时,通常使用全限定名来引用类和接口。这是因为这些代码需要具有明确性,以便能够在不同环境和上下文中安全运行。
-
在底层库中,包名和类名可能与用户自定义的类产生冲突,因此使用全限定名是确保准确性和安全性的一种有效方式。
-
顶层源码与全限定名
-
顶层源码的规范:Java 的顶层类(如枚举、接口等)通常遵循某种命名规范和层级结构,这种结构通过包名来组织。全限定名反映了这种结构,使得每个类或接口的引用都具有独特性。
-
一般约定:在 Java 开发中,使用全限定名是一个好的实践,特别是在以下情况下:
-
引用不在当前包中的类。
-
避免使用简单类名以防止命名冲突。
-
提高代码的可读性和可维护性。
-
总结
使用全限定名是 Java 中一种重要的实践,它确保了类的唯一性,避免了命名冲突,并提高了代码的可读性和清晰性。特别是在底层代码或大型项目中,这种写法是非常必要的,以确保开发者能够准确地理解代码的来源和功能。
枚举的相关方法
案例:
package com.enumeration.learn;
/*** @author 朱慧军* @version 1.0*/
public class Learn03 {public static void main(String[] args) {
// 创建枚举类型 General 的实例General general = General.ordinary;General general2 = General.excellent;
// 打印枚举实例,会自动调用枚举的 toString() 方法System.out.println(general);System.out.println(general2);
// 调用 name() 方法,返回当前枚举实例的名称System.out.println("使用底层的name()" + general.name());
// 调用 ordinal() 方法,返回枚举实例在定义时的顺序,从 0 开始System.out.println("使用底层的ordinal(): " + general2.ordinal());
// 使用 values() 方法遍历枚举所有实例for (General gen : General.values()) {System.out.println(gen);}
System.out.println("-----------------------------------------");
// 使用 valueOf() 方法将字符串转换为对应的枚举实例General general02 = General.valueOf("excellent");if (general02 == General.excellent) {System.out.println("数据查找到 " + general02);} else {System.out.println("数据不存在");}
// 使用 compareTo() 方法比较两个枚举的定义顺序// 计算 general 和 general2 的序数差值(ordinal 0 - ordinal 1)System.out.println(general.compareTo(general2));/** compareTo 方法的底层实现通过两个枚举实例的序数(ordinal)差值进行比较。* 如果枚举类型不同,则会抛出 ClassCastException 异常。*/
}
}
// 定义枚举类 General,底层继承了 Enum 类
enum General {// 枚举常量,使用无参构造器初始化ordinary, excellent;
}
Enum 类的 valueOf 方法的实现源码
用于将一个指定名称的字符串转换为对应的枚举常量。具体实现和作用如下:
public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {// 尝试从 enumType 中查找名为 name 的枚举常量T result = enumType.enumConstantDirectory().get(name);// 如果查找成功,返回对应的枚举常量if (result != null)return result;// 如果 name 参数为 null,则抛出 NullPointerException 异常if (name == null)throw new NullPointerException("Name is null");// 如果没有找到对应的枚举常量且 name 不是 null,则抛出 IllegalArgumentException 异常,// 并提示用户没有该枚举常量throw new IllegalArgumentException("No enum constant " + enumType.getCanonicalName() + "." + name);
}
代码解析
-
泛型声明
<T extends Enum<T>>:-
此方法是一个泛型方法,
<T extends Enum<T>>表示该方法适用于任何枚举类型T,且T必须是Enum类的子类。 -
这使得
valueOf方法可以用于所有具体的枚举类,而不仅限于某个特定枚举。 -
在 Java 中,
<T extends Enum<T>> T valueOf(Class<T> enumType, String name)是一种泛型方法声明,意味着这个方法适用于任何枚举类型T,并提供了特定的类型安全。让我们逐步分析这段声明的具体含义。泛型方法声明分析
1.
<T extends Enum<T>>这部分是泛型方法的类型参数声明。
-
T是一个类型参数,代表枚举类型(比如Color、Day等枚举类类型)。 -
extends Enum<T>指明T必须是Enum类的子类,或者更准确地说,T必须是某种枚举类型。因为所有枚举类型在 Java 中都隐式地继承了Enum类,所以T只能是枚举类型。 -
<T extends Enum<T>>这种形式在泛型类型约束上很特殊,它不仅限定了T为Enum的子类型,还要求T自身是一个泛型类型。这种设计用于支持 Java 的枚举类型系统,使枚举类可以成为泛型类型的参数。
2.
T valueOf(...)-
T作为返回类型,表示该方法返回的类型与输入的枚举类型T相同。例如,如果T是Color(如Color.RED),那么方法返回的类型就是Color。
3.
Class<T> enumType-
Class<T>表示enumType参数是一个Class对象,它表示我们要查找的枚举类型的类信息(即Color.class、Day.class等)。 -
enumType参数允许方法在运行时知道具体的枚举类型。Class<T>表示enumType必须是枚举类型T的Class对象。
4.
String name-
name是一个String类型的参数,表示要查找的枚举常量的名称。此名称必须与枚举常量的名称严格匹配(包括大小写),否则会抛出IllegalArgumentException。
具体含义
整体来看,
<T extends Enum<T>> T valueOf(Class<T> enumType, String name)方法的含义是:-
这是一个泛型方法,适用于所有枚举类型。
-
方法接受一个枚举类型
enumType和一个枚举常量名称name。 -
该方法会在
enumType指定的枚举类型中查找名称为name的常量,并返回对应的枚举常量。 -
如果找不到匹配的常量,或者
name为null,则会抛出相应的异常。
示例代码解析
让我们看一个例子来理解
valueOf方法如何工作。假设我们有一个枚举类型
Color:enum Color {RED, GREEN, BLUE }调用
valueOf方法时,T是Color类型:Color color = Enum.valueOf(Color.class, "RED"); // 返回 Color.RED
-
T被推断为Color。 -
enumType参数是Color.class,表示我们在Color枚举类型中查找。 -
name参数是"RED",表示我们要找的常量的名称。 -
valueOf方法会在Color枚举中查找"RED"常量,找到后返回Color.RED。
如果名称不匹配或为
null,则会抛出异常:Color color = Enum.valueOf(Color.class, "YELLOW"); // 抛出 IllegalArgumentException Color color2 = Enum.valueOf(Color.class, null); // 抛出 NullPointerException
主要优点
使用泛型方法
<T extends Enum<T>>的主要优点是:-
类型安全:这种泛型约束使得编译器可以确保
valueOf方法只在枚举类型上使用,避免了不正确的使用。 -
代码通用性:通过泛型方法声明,
valueOf方法可以适用于任何枚举类型,而无需为每个枚举编写不同的方法。
这种声明不仅在 Java 枚举类型中被广泛使用,也能在其他泛型编程中实现类似的类型安全和通用性。
-
-
-
参数
Class<T> enumType:-
这个参数表示枚举类型的
Class对象,即我们想要查找的枚举类型。 -
enumType提供了枚举类的类型信息,使方法可以在该类型的范围内进行搜索。
-
-
参数
String name:-
这个参数表示要查找的枚举常量的名称。名称必须完全匹配(区分大小写),否则会报错。
-
如果
name为null,则抛出NullPointerException异常(详见后面的逻辑)。
-
-
查找枚举常量:
-
通过
enumType.enumConstantDirectory()获取一个哈希表,其中保存了该枚举类型的所有常量名称和对应的枚举实例。 -
使用
name作为键,从enumConstantDirectory中查找对应的枚举常量并赋给result。
-
-
返回结果或抛出异常:
-
如果
result不为null,则表示找到了对应的枚举常量,直接返回。 -
如果
result为null且name为null,抛出NullPointerException异常,表示名称不能为空。 -
如果
result为null且name不是null,抛出IllegalArgumentException异常,并包含错误提示信息,告知用户没有找到对应的枚举常量。
-
使用场景
valueOf 方法通常用于将一个字符串转换成指定名称的枚举常量,如在解析配置文件或处理用户输入时,根据字符串名称动态地获取枚举实例。
示例
假设我们有如下枚举:
enum Color {RED, GREEN, BLUE
}
调用示例:
Color color = Enum.valueOf(Color.class, "RED"); // 返回 Color.RED Color color2 = Enum.valueOf(Color.class, "YELLOW"); // 抛出 IllegalArgumentException Color color3 = Enum.valueOf(Color.class, null); // 抛出 NullPointerException
这种实现确保了 valueOf 方法能精确匹配指定名称的枚举常量,并处理无效名称和空名称的情况。
enumType 是一个 Class<T> 类型的对象,
表示一个具体的枚举类。在 Java 中,Class<T> 对象包含了一个类的各种元数据和方法,包括其名称、包、方法、字段等。对于枚举类来说,enumType 表示该枚举的类对象。
在 Enum.valueOf 方法中的 enumType
在 Enum.valueOf(Class<T> enumType, String name) 方法中:
-
enumType表示一个指定的枚举类。 -
例如,假设有一个枚举类
Day,调用Day.valueOf(Day.class, "MONDAY")时,enumType就表示Day.class,它存储了所有Day枚举相关的信息。
这个 enumType 的作用是提供上下文,告诉 valueOf 方法需要在哪个枚举类中查找常量。在这个方法中,可以通过 enumType 来获取 enumConstantDirectory(),从而查找该枚举类中的常量。
示例说明
假设有一个枚举类:
enum Day {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
当调用:
Day day = Enum.valueOf(Day.class, "MONDAY");
-
Day.class就是传递的enumType。 -
valueOf方法会使用enumType来找到Day类的enumConstantDirectory(),并从中获取"MONDAY"对应的常量实例Day.MONDAY。
通过这种方式,enumType 让 valueOf 方法可以在指定的枚举类中查找并返回相应的枚举常量实例。
在 Enum<?> other = (Enum<?>) o; 和 Enum<E> self = this; 这两行代码中,<?> 和 <E> 是泛型的符号,代表不同的泛型使用方式,它们分别有不同的含义。下面逐个解释。
1. Enum<?> other = (Enum<?>) o;
-
<?>:这是一个通配符(wildcard)类型,表示任何类型的Enum类型。-
Enum<?>代表的是某个枚举类的类型,但并不关心具体是哪一个枚举类。 -
使用
<?>时,表示这个Enum类型的实例可以是任何具体枚举类的实例,而不指定具体的枚举类型。
-
-
Enum<?> other = (Enum<?>) o;:-
这行代码的意思是将传入的对象
o强制转换为Enum<?>类型。 -
Enum<?>表示它是一个枚举类型的实例,但它的具体类型可以是任何的枚举类型,具体类型并不重要。 -
<?>主要用于方法中的不确定类型,表示这是一个未知类型的枚举类,且我们只关心它是枚举类的实例。
-
2. Enum<E> self = this;
-
<E>:这是一个类型参数(type parameter),代表枚举类的具体类型,E是一个泛型类型变量。-
Enum<E>代表的是具体的枚举类,这里的E将会是实际的枚举类类型(如Day、Color等)。 -
E是一个占位符,表示某种类型,它会在调用时被替换为具体的类型。
-
-
Enum<E> self = this;:-
self是当前对象(即调用compareTo的枚举实例),它的类型是Enum<E>。 -
E是泛型类型参数,它代表当前枚举类的类型。换句话说,self是某个具体枚举类型的实例,且该类型是由E来表示的。 -
例如,如果枚举类型是
Day,那么E就会被替换为Day,Enum<E>就变成Enum<Day>。
-
具体总结:
-
<?>:是一个通配符,表示不指定具体的类型,可以是任何类型的Enum,常用于泛型方法中当你不关心具体类型时。 -
<E>:是一个类型参数,表示当前类或方法的类型(即枚举类的类型),它会在编译时被替换为实际的类型。 -
<T>是另一个常见的类型参数,它也表示一个占位符,表示某种类型。T常用于一般情况下的类型变量,表示任意类型。-
例如,
public class Container<T>表示一个泛型类Container,它的类型是T,T代表一个具体的类型(例如Integer、String等)
-
在 compareTo 方法中,这两个泛型符号的结合允许我们比较不同枚举类型的实例,确保类型安全并进行必要的比较。
general.compareTo(general2) 这行代码调用了 Enum 类中的 compareTo 方法来比较两个枚举值 general 和 general2。让我们仔细分析这段代码中 compareTo(E o) 的作用和如何执行。
1. compareTo(E o) 方法
compareTo(E o) 方法是 Enum 类(enum 枚举类型的父类)的一部分,继承自 Comparable<E> 接口。它的目的是比较两个枚举常量的顺序。其具体实现如下:
public final int compareTo(E o) {Enum<?> other = (Enum<?>)o;Enum<E> self = this; // `General` 枚举类就是 `E` 的具体类型。this就是你调用的compareTo的对象if (self.getClass() != other.getClass() && // optimizationself.getDeclaringClass() != other.getDeclaringClass())throw new ClassCastException();return self.ordinal() - other.ordinal();
}
参数 E o:
-
E是Enum类型的泛型参数,它表示任何具体的枚举类型。在你的代码中,General枚举类就是E的具体类型。 -
o是要比较的枚举常量,在这段代码中,它是general2,即General.excellent。
2. general.compareTo(general2) 的执行过程
假设 general = General.ordinary,general2 = General.excellent,在执行 general.compareTo(general2) 时,过程如下:
-
传递参数:
general是General.ordinary,general2是General.excellent。-
compareTo方法的参数o就是general2(即General.excellent)。
-
-
类型转换:
-
Enum<?> other = (Enum<?>)o;这一行代码将传入的o强制转换为Enum<?>类型。Enum<?>是一个包含所有枚举类类型的父类,所以这里的转换是合法的。
-
-
检查类型匹配:
-
if (self.getClass() != other.getClass() && self.getDeclaringClass() != other.getDeclaringClass())用于检查两个枚举常量是否属于同一个枚举类。它会首先检查self.getClass()和other.getClass()是否相同。如果类型不同,则抛出ClassCastException异常。为了避免不必要的错误,这种检查会优化代码执行。
-
-
执行比较:
-
return self.ordinal() - other.ordinal();这一行代码是核心,它通过调用枚举常量的ordinal()方法来获取它们的顺序。ordinal()方法返回该枚举常量在枚举类中定义的顺序,从0开始。-
对于
General.ordinary,ordinal()返回0。 -
对于
General.excellent,ordinal()返回1。
-
-
-
返回结果:
-
比较结果是
self.ordinal() - other.ordinal()。即0 - 1 = -1。根据compareTo方法的返回值:-
返回
0表示两个枚举常量相同。 -
返回负数表示
self在other之前。 -
返回正数表示
self在other之后。
-
-
在这个例子中,返回
-1,表示General.ordinary在General.excellent之前。
-
3. 总结
-
compareTo(E o)方法用于比较两个枚举常量的顺序,它通过ordinal()方法获取枚举常量在枚举类中的定义顺序。 -
general.compareTo(general2)实际上是调用Enum.compareTo(general2),比较general和general2的ordinal值,进而确定它们的顺序。
在 Java 中,E 是泛型类型参数,用于表示一个类型,可以是任何类型,特别是在枚举类的上下文中,它表示某种具体的枚举类型。要理解为什么 General 枚举类就是 E 的具体类型,我们需要理解 Enum<E> 类的泛型机制。
1. Enum<E> 类的定义
Enum 是 Java 中所有枚举类型的基类。它是一个泛型类,定义为:
public abstract class Enum<E extends Enum<E>> extends Object implements Comparable<E>, Serializable
-
这个类使用了泛型参数
E,它限定了E必须是Enum<E>的子类,也就是某个具体的枚举类类型。 -
Enum<E>作为枚举类的基类,它的泛型参数E会被具体化为枚举类型(如General)在使用时的实际类型。
2. Enum<?> 和 Enum<E> 中的泛型参数
在枚举类的实现中,Enum<?> 和 Enum<E> 代表了不同的类型:
-
Enum<?>是一个通配符,它可以代表任何枚举类型的对象。 -
Enum<E>是一个具体的枚举类型E,例如General,也就是说它代表的是General类型的枚举对象。
3. compareTo 方法中的泛型类型
让我们看看 compareTo(E o) 方法的定义:
public final int compareTo(E o) {Enum<?> other = (Enum<?>)o;Enum<E> self = this;if (self.getClass() != other.getClass() && self.getDeclaringClass() != other.getDeclaringClass())throw new ClassCastException();return self.ordinal() - other.ordinal();
}
-
E是泛型类型:E是Enum<E>中的泛型类型,它会在具体的枚举类中被替换为某个枚举类型(那个枚举类型的对象调用的就是那个枚举类型),比如General。 -
this和o:this表示当前对象,是Enum<E>类型的对象(具体是General的一个实例),而o是方法的参数,它是另一个Enum<E>类型的对象(具体是General的另一个实例)。 -
Enum<E> self = this;:这行代码将this转换为Enum<E>类型,因为this就是当前枚举对象,它的类型是Enum<General>,所以self的类型是Enum<General>。 -
Enum<?> other = (Enum<?>)o;:这行代码将传入的o强制转换为Enum<?>,表示它是一个任意类型的枚举对象。在调用compareTo时,o的类型与this相同,都是General类型的枚举常量。
4. general.compareTo(general2) 如何传递类型
在你写的代码中,调用 general.compareTo(general2) 时:
General general = General.ordinary; General general2 = General.excellent; System.out.println(general.compareTo(general2)); // 调用 compareTo
-
general是General.ordinary(General枚举中的一个常量)。 -
general2是General.excellent(General枚举中的另一个常量)。
当你调用 general.compareTo(general2) 时:
-
general的类型是General,它继承自Enum<General>。 -
general2也是General类型的枚举常量,因此它也是Enum<General>类型。 -
在执行
compareTo(general2)时,o的类型就是General类型,E会被替换为General,也就是说,compareTo方法的E就是General,并且会进行相应的比较。
5. 结论
在 compareTo(E o) 方法中,E 是一个泛型类型,它代表的是枚举类型。在你调用 general.compareTo(general2) 时,E 会被替换为具体的 General 类型。general 和 general2 都是 General 类型的枚举常量,它们的类型就是 Enum<General>,因此在 compareTo 方法中,Enum<E> 被具体化为 Enum<General>,从而可以使用 ordinal() 方法比较它们的顺序。
理解 this 和 General 枚举类在 compareTo(E o) 方法中的关系。
1. Enum<E> 中的泛型 E
首先,Enum<E> 是一个泛型类,它的泛型类型 E 是枚举类的具体类型。例如,如果你定义一个枚举类 General:
enum General {ordinary, excellent;
}
General 就是 E 的具体类型,也就是 Enum<E> 中的 E 被替换为 General 类型。因此,General 是 Enum<E> 中的实际类型,而 Enum<E> 是 Java 中所有枚举类型的基类。
2. compareTo(E o) 中的 this 和 o
compareTo(E o) 是一个 Enum 类中的方法,用来比较两个枚举值的顺序。
当你在 General 枚举类的对象上调用 compareTo 方法时,this 代表的是当前调用 compareTo 的枚举对象,而 o 代表的是你传入的参数,它也是一个枚举对象。
举个例子:
General general1 = General.ordinary; General general2 = General.excellent; int result = general1.compareTo(general2);
在这段代码中:
-
this指的是general1,也就是General.ordinary。 -
o指的是general2,也就是General.excellent。
3. compareTo(E o) 的执行过程
让我们看看 compareTo(E o) 的实现:
public final int compareTo(E o) {Enum<?> other = (Enum<?>) o;Enum<E> self = this;if (self.getClass() != other.getClass() &&self.getDeclaringClass() != other.getDeclaringClass()) {throw new ClassCastException();}return self.ordinal() - other.ordinal();
}
a. this 和 o 的类型
-
this是调用compareTo的当前枚举对象,类型是Enum<E>,具体到你的例子,就是Enum<General>。 -
o是传入的参数,它也是一个枚举对象,类型也是Enum<E>,具体到你的例子,o的类型也是Enum<General>。
b. Enum<?> other = (Enum<?>) o;
这行代码将 o 强制转换为 Enum<?>,表示它是一个枚举类型的对象。<?> 是通配符,代表了 o 可以是任何枚举类型的实例。
c. Enum<E> self = this;
这行代码将 this 转换为 Enum<E> 类型。因为 this 是当前的枚举对象,在 General 枚举类中,E 被具体化为 General 类型,所以 self 的类型就是 Enum<General>。
d. 比较顺序:self.ordinal() - other.ordinal()
ordinal() 方法返回枚举值的顺序(即它在枚举类中声明的顺序)。compareTo 方法通过 ordinal() 的差值来比较两个枚举值的顺序:
-
如果
self.ordinal()小于other.ordinal(),说明self在枚举类中出现得更靠前,返回一个负数。 -
如果
self.ordinal()等于other.ordinal(),说明它们的顺序相同,返回 0。 -
如果
self.ordinal()大于other.ordinal(),说明self在枚举类中出现得更靠后,返回一个正数。
4. 总结
-
this是调用compareTo方法的当前枚举对象。 -
o是传入的枚举对象,它也会是Enum<E>类型,具体来说是Enum<General>。 -
General枚举类是Enum<E>中的具体类型,因此在compareTo方法中,E会被具体化为General类型,this和o都是General类型的枚举值。
通过这种方式,compareTo 方法实现了对枚举值顺序的比较,使得你可以直接比较不同的 General 枚举常量的顺序。