Java枚举与泛型
前言
本节给大家讲讲 枚举与泛型 ,本篇内容主要包括以下几个部分:
- 枚举定义和使用
- 泛型讲解
- 代码实操
- 常见的泛型问题
枚举
定义
啥是枚举?从定义来讲枚举就是情况的各种罗列,在java中它是一种特殊的类,一般表示一组常量,举个例子:
enum Color { RED, BLUE; } 复制代码
上述代码,枚举了颜色的两种成员,分别是 RED, BLUE
,枚举也可以定义在类中,我们输出一下:
public class EnumTest { enum Color { RED, GREEN, BLUE; } // 执行输出结果 public static void main(String[] args) { Color c = Color.RED; System.out.println(c); // RED } } 复制代码
结果返回 RED
遍历枚举
如何去遍历它的成员呢?
public class EnumTest { enum Color { RED, GREEN, BLUE; } // 执行输出结果 public static void main(String[] args) { for (Color c : Color.values()) { System.out.println(c); // RED // GREEN // BLUE } } } 复制代码
作为条件使用
我们也可以用作条件使用,这样代码可读性会变高一点
public class EnumTest { enum Color { RED, GREEN, BLUE; } // 执行输出结果 public static void main(String[] args) { Color c = Color.BLUE; switch(c) { case RED: System.out.println("红色"); break; case GREEN: System.out.println("绿色"); break; case BLUE: System.out.println("蓝色"); break; } } } 复制代码
进阶用法
举一个常用的例子,我们在做API请求返回响应结果时,我们可以利用枚举去定义我们的响应体,根据不同的变量,返回响应的结果。来看一个我写好的例子:
public enum Response implements ResponseInter { SUCCESS(200, null, "success"), Fail(0, null, "错误"), Response(0, null, ""); private String msg; private Integer code; private Object data; Response(int code, String data, String msg) { this.code = code; this.msg = msg; this.data = data; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } @Override public ResponseData response(int code, String data, String msg) { this.code = code; this.msg = msg; this.data = data; return getResponse(); } private ResponseData getResponse() { ResponseData response = new ResponseData(); response.setData(this.data); response.setCode(this.code); response.setMsg(this.msg); return response; } public ResponseData success() { return getResponse(); } public ResponseData fail() { return getResponse(); } } 复制代码
枚举也是一种特殊的类 ,所以它也可以实现接口。 当我们定义 SUCCESS(200, null, "success")
这种带括号的成员时,idea
会自动提示你去创建它的构造函数,所以后边我们可以取实例化它。
@FunctionalInterface // 函数式接口 public interface ResponseInter { ResponseData response(int code, String data, String msg); } 复制代码
这里插一个小知识点,@FunctionalInterface
表示函数式接口, 成员内部只能有一个接口方法。
如何使用?
public class EnumMain { public static void main(String[] args) { Response response = Response.SUCCESS; String res = JSON.toJSONString(response.success()); Log.info(res); // {"code":200,"msg":"success"} Response response1 = Response.Fail; String res1 = JSON.toJSONString(response1.fail()); Log.info(res1); //{"code":0,"msg":"错误"} Response response2 = Response.Response; String res2 = JSON.toJSONString(response2.response(200, "这是数据", "success")); Log.info(res2); // {"code":200,"data":"这是数据","msg":"success"} } } 复制代码
是不是很简单,这里我只是粗略的定义了一下,其实还可以继续深入下去,比如我们定义各种情况下请求的状态码,这样一来,就不用我们每次手动填写具体的值了。
泛型
从字面意思讲,不确定的类型。有时候我们在写代码时,传参都需要定义具体的类型,不然编译会报错,但是有些情况我们需要复用我们的代码,但是由于强类型的要求下,我们不得已去在定义这样的类,这样显得很麻烦,所以这时候泛型,就有很大的作用了。
定义
先看最简单的一个例子体会一下:
@Data public class ClassMain <T> { private T name; } 复制代码
泛型类 型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。 通常使用T来代表某种类型, T不是绝对的,可以是任意。
如何使用, 类名<类型>:
ClassMain<String> f = new ClassMain<>(); f.setName("haha"); Log.info(f.getName().getClass()); // class java.lang.String 复制代码
泛型接口
泛型接口使用还是比较多的,看一个例子:
public interface InterfaceMain<T> { T hello(ClassMain<T> ...obj); } 复制代码
@Data public class ClassMain <T> implements InterfaceMain <T> { private T name; @Override public T hello(ClassMain<T> ...obj) { return this.name; } } 复制代码
...obj
这个表示不定参数,意思是说你可以传多个同类型的参数。
泛型通配符
有时候你在使用别人写好的类时,经常看到?
这样的标记, 它表示一些不确定的类型:
// 泛型通配符 // 适合一些不确定的类型 public T hello1(ClassMain<?> obj) { return this.name; } 复制代码
泛型方法
通过一个例子感受一下:
public class MethodMain <T> { private T name; public <T> T hello(MethodMain<T> obj) { return obj.getMsg(obj.name); } public T getMsg(T name) { return name; } public <T> void getMsg(T ...arg) { for(T args :arg) { Log.info("泛型可变参 --->" + args); } } // 静态方法使用泛型时,需要定义成泛型方法, 并且与定义类无关 public static <T> void get1(T ...arg) { Log.info(arg.getClass().getName()); } // 泛型中的边界 // extends 继承 String 类型 // 还有一个是 super 反之 public static <T extends String> void get2(T ...arg) { Log.info(arg.getClass().getName()); } } 复制代码
这种通常叫 泛型边界, 常用来约束我们的泛型,使它严谨一些。
泛型一问一答❓
- Java中的泛型是什么 ? 使用泛型的好处是什么?
泛型防止了那种情况的发生,它提供了编译期的类型安全,确保你只能把正确类型的对象放入集合中,避免了在运行时出现ClassCastException
。
- Java的泛型是如何工作的 ? 什么是类型擦除 ?
泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。
- 什么是泛型中的限定通配符和非限定通配符 ?
限定通配符对类型进行了限制。有两种限定通配符,一种是它通过确保类型必须是T的子类来设定类型的上界,另一种是
它通过确保类型必须是T的父类来设定类型的下界。泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。另一方面
表示了非限定通配符,因为
可以用任意类型来替代。
List
和List
之间有什么区别 ❓
这两个List的声明都是限定通配符的例子,List
可以接受任何继承自T的类型的List,而List
可以接受任何T的父类构成的List
。例如List
可以接受List
或List
。
- 如何编写一个泛型方法,让它能接受泛型参数并返回泛型类型?
编写泛型方法并不困难,你需要用泛型类型来替代原始类型,比如使用T, E or K,V
等被广泛认可的类型占位符。 最简单的情况下,一个泛型方法可能会像这样:
public V put(K key, V value) { return cache.put(key, value); } 复制代码
- 编写一段泛型程序来实现LRU缓存?
LinkedHashMap
可以用来实现固定大小的LRU缓存,当LRU缓存已经满了的时候,它会把最老的键值对移出缓存。LinkedHashMap提供了一个称为removeEldestEntry()
的方法,该方法会被put()和putAll()调用来删除最老的键值对。
- 你可以把
List
传递给一个接受List参数的方法吗?
因为乍看起来
String
是一种Object
,所以List
应当可以用在需要List的地方,但是事实并非如此。真这样做的话会导致编译错误。如果你再深一步考虑,你会发现Java这样做是有意义的,因为
List可以存储任何类型的对象包括
String, Integer
等等,而List
却只能用来存储String
。List<Object> objectList; List<String> stringList; objectList = stringList; //compilation error incompatible types 复制代码
Array
中可以用泛型吗?
前提是你要知道Array事实上并不支持泛型,因为List可以提供编译期的类型安全保证,而Array却不能。