List<String> errors = new ArrayList<>(); Map<String, String> paramRules = Map.of( "phoneOrAccount", "账号不能为空", "accountType", "账号类型不能为空", "deviceCode", "机器码不能为空", "code", "图形验证码不能为空", "verifyCode", "短信验证码不能为空" );改为java8支持的方法
时间: 2025-06-18 09:14:07 浏览: 12
<think>由于用户要求将使用`Map.of`方法的代码改为Java8支持的方法,而`Map.of`是Java9引入的,在Java8中不可用。因此,我们需要使用其他方式初始化一个不可变的Map。在Java8中,我们可以使用以下几种方法替代`Map.of`:1.使用`Collections.singletonMap`:但只能创建单个键值对的Map。2.使用匿名内部类:创建一个匿名类并初始化,但这种方式较为繁琐。3.使用Stream:通过Stream的`collect`方法收集到Map,但需要多个步骤。4.使用`newHashMap`并配合静态代码块或静态方法:然后使用`Collections.unmodifiableMap`包装成不可变Map。但是,由于我们需要创建多个键值对,且希望是不可变的Map,在Java8中常用的方式是使用静态初始化块或者使用工具类来构建。实际上,我们可以使用GoogleGuava库的`ImmutableMap`,但用户可能没有使用该库。因此,我们考虑使用纯Java8的方式。这里,我们可以采用双括号初始化(不推荐,因为会创建匿名类)或者使用一个工具方法来构建。但是,更常见的做法是使用静态初始化器。然而,在代码中直接内联初始化一个不可变的Map,我们可以这样:```javaMap<String,String>paramRules=newHashMap<>();paramRules.put("phoneOrAccount","账号不能为空");paramRules.put("accountType","账号类型不能为空");paramRules.put("deviceCode","机器码不能为空");paramRules.put("code","图形验证码不能为空");paramRules.put("verifyCode","短信验证码不能为空");Map<String,String>unmodifiableParamRules=Collections.unmodifiableMap(paramRules);```但是,这样写代码量较多,而且我们创建了一个可变的HashMap,然后再包装成不可变Map。另一种方式是使用Java8的StreamAPI,但这样写起来也不够简洁。实际上,在Java8中,我们通常这样处理:```javaMap<String,String>paramRules=Collections.unmodifiableMap(newHashMap<String,String>(){{put("phoneOrAccount","账号不能为空");put("accountType","账号类型不能为空");put("deviceCode","机器码不能为空");put("code","图形验证码不能为空");put("verifyCode","短信验证码不能为空");}});```这里使用了双括号初始化,但是需要注意,这种方式会创建匿名内部类,可能会引起内存泄漏,且序列化可能会有问题。不过,在方法内部作为局部变量使用,通常问题不大。如果不想使用双括号初始化,我们可以拆分步骤:```javaMap<String,String>map=newHashMap<>();map.put("phoneOrAccount","账号不能为空");map.put("accountType","账号类型不能为空");map.put("deviceCode","机器码不能为空");map.put("code","图形验证码不能为空");map.put("verifyCode","短信验证码不能为空");Map<String,String>paramRules=Collections.unmodifiableMap(map);```但是,这样代码行数较多。另外,我们也可以使用数组和循环来初始化,但这样更繁琐。考虑到代码的简洁性和可读性,在局部变量中使用双括号初始化是相对常见的选择。不过,如果追求更好的实践,我们可以使用一个辅助方法:```javaprivatestaticMap<String,String>createMap(){Map<String,String>map=newHashMap<>();map.put("phoneOrAccount","账号不能为空");map.put("accountType","账号类型不能为空");map.put("deviceCode","机器码不能为空");map.put("code","图形验证码不能为空");map.put("verifyCode","短信验证码不能为空");returnCollections.unmodifiableMap(map);}```然后在需要的地方调用:```javaMap<String,String>paramRules=createMap();```但是,由于这个Map只在当前方法中使用,单独写一个辅助方法可能不太合适。因此,根据用户代码上下文,我们选择使用双括号初始化,并包装为不可变Map。但是,请注意双括号初始化的潜在问题。然而,在用户代码中,这个Map只是作为局部变量使用,并且很快就会被垃圾回收,所以使用双括号初始化是可以接受的。因此,修改后的代码如下:```javaMap<String,String>paramRules=Collections.unmodifiableMap(newHashMap<String,String>(){{put("phoneOrAccount","账号不能为空");put("accountType","账号类型不能为空");put("deviceCode","机器码不能为空");put("code","图形验证码不能为空");put("verifyCode","短信验证码不能为空");}});```这样,我们就创建了一个不可变的Map,并且内容与之前使用`Map.of`相同。但是,请注意,在Java9及以上版本中,`Map.of`是更好的选择,因为它更简洁且没有匿名类的问题。这里只是为了兼容Java8。另外,如果用户希望避免双括号初始化,也可以使用以下方式:```javaMap<String,String>paramRules=newHashMap<>();paramRules.put("phoneOrAccount","账号不能为空");paramRules.put("accountType","账号类型不能为空");paramRules.put("deviceCode","机器码不能为空");paramRules.put("code","图形验证码不能为空");paramRules.put("verifyCode","短信验证码不能为空");```然后,在后续使用中,注意不要修改这个Map(尽管它不是不可变的,但只要我们不在其他地方修改,也可以接受)。或者,我们也可以不包装成不可变,因为在这个自定义认证提供者中,这个Map只被读取一次,而且是在同一个线程中,所以即使不是不可变的,只要不被修改,也是安全的。但是,为了更符合函数式编程的习惯,我们通常希望它是不可变的。因此,我们还是使用`Collections.unmodifiableMap`来包装。综上所述,我们采用双括号初始化并包装成不可变Map的方式。现在,将这段代码放入原来的上下文中:```java@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{http.csrf().disable();//自定义认证提供者AuthenticationProviderparamValidationProvider=newAuthenticationProvider(){@OverridepublicAuthenticationauthenticate(Authenticationauthentication)throwsAuthenticationException{HttpServletRequestrequest=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();//仅对特定请求进行参数校验if("/oauth/app/verifyCode/token".equals(request.getRequestURI())&&"POST".equalsIgnoreCase(request.getMethod())){//创建参数校验规则Map(Java8兼容写法)Map<String,String>paramRules=Collections.unmodifiableMap(newHashMap<String,String>(){{put("phoneOrAccount","账号不能为空");put("accountType","账号类型不能为空");put("deviceCode","机器码不能为空");put("code","图形验证码不能为空");put("verifyCode","短信验证码不能为空");}});List<String>errors=newArrayList<>();//执行参数校验paramRules.forEach((param,errorMsg)->{if(isEmpty(request.getParameter(param))){errors.add(errorMsg);}});//抛出参数校验异常if(!errors.isEmpty()){thrownewBadCredentialsException(String.join(";",errors));}}returnauthentication;}@Overridepublicbooleansupports(Class<?>authentication){returntrue;//支持所有认证类型}};//...其余代码保持不变...}```这样,我们就将`Map.of`的初始化替换为Java8兼容的写法。注意:双括号初始化会生成一个匿名内部类,这可能会增加一点内存开销,但在这种局部使用且只创建一次的场景下,影响不大。另外,我们也可以将`paramRules`提取为静态常量,以避免每次调用都创建。但是,由于这个规则只在特定请求中才使用,而且规则不会改变,所以提取为常量也是可以的。但是,考虑到这个内部类是在`configure`方法中,我们可以将其提取为外部类的静态成员变量。修改如下:在类`SecurityConfig`中添加:```javaprivatestaticfinalMap<String,String>PARAM_RULES;static{Map<String,String>map=newHashMap<>();map.put("phoneOrAccount","账号不能为空");map.put("accountType","账号类型不能为空");map.put("deviceCode","机器码不能为空");map.put("code","图形验证码不能为空");map.put("verifyCode","短信验证码不能为空");PARAM_RULES=Collections.unmodifiableMap(map);}```然后在`authenticate`方法中直接使用`PARAM_RULES`:```javaList<String>errors=newArrayList<>();//执行参数校验PARAM_RULES.forEach((param,errorMsg)->{if(isEmpty(request.getParameter(param))){errors.add(errorMsg);}});```这样,避免了每次请求都创建Map,且代码更清晰。综上所述,我们选择将`paramRules`提取为静态常量,这样既高效又避免了双括号初始化的潜在问题。因此,最终修改后的代码如下:在`SecurityConfig`类中定义静态常量:```javaprivatestaticfinalMap<String,String>PARAM_RULES;static{Map<String,String>map=newHashMap<>();map.put("phoneOrAccount","账号不能为空");map.put("accountType","账号类型不能为空");map.put("deviceCode","机器码不能为空");map.put("code","图形验证码不能为空");map.put("verifyCode","短信验证码不能为空");PARAM_RULES=Collections.unmodifiableMap(map);}```然后在`authenticate`方法中使用:```java@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{http.csrf().disable();//自定义认证提供者AuthenticationProviderparamValidationProvider=newAuthenticationProvider(){@OverridepublicAuthenticationauthenticate(Authenticationauthentication)throwsAuthenticationException{HttpServletRequestrequest=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();//仅对特定请求进行参数校验if("/oauth/app/verifyCode/token".equals(request.getRequestURI())&&"POST".equalsIgnoreCase(request.getMethod())){List<String>errors=newArrayList<>();//执行参数校验PARAM_RULES.forEach((param,errorMsg)->{if(isEmpty(request.getParameter(param))){errors.add(errorMsg);}});//抛出参数校验异常if(!errors.isEmpty()){thrownewBadCredentialsException(String.join(";",errors));}}returnauthentication;}@Overridepublicbooleansupports(Class<?>authentication){returntrue;//支持所有认证类型}};//...其余代码保持不变...}```这样,我们既实现了Java8的兼容,又优化了性能(避免了每次请求都创建Map),同时避免了双括号初始化的缺点。</think>在 Java 8 中,可以使用以下方法替代 `Map.of()` 来初始化不可变映射:
### 1. 使用 `Collections.singletonMap()`(单键值对)
```java
// 创建单个键值对的不可变映射
Map<String, String> paramRules = Collections.singletonMap("key", "value");
```
### 2. 使用匿名内部类初始化(多键值对)
```java
// 创建多键值对的不可变映射
Map<String, String> paramRules = new HashMap<String, String>() {{
put("phoneOrAccount", "账号不能为空");
put("accountType", "账号类型不能为空");
put("deviceCode", "机器码不能为空");
put("code", "图形验证码不能为空");
put("verifyCode", "短信验证码不能为空");
}};
```
### 3. 使用静态初始化块(推荐)
```java
// 在类中声明静态常量
private static final Map<String, String> PARAM_RULES;
static {
Map<String, String> map = new HashMap<>();
map.put("phoneOrAccount", "账号不能为空");
map.put("accountType", "账号类型不能为空");
map.put("deviceCode", "机器码不能为空");
map.put("code", "图形验证码不能为空");
map.put("verifyCode", "短信验证码不能为空");
PARAM_RULES = Collections.unmodifiableMap(map);
}
// 在方法中使用
List<String> errors = new ArrayList<>();
PARAM_RULES.forEach((param, errorMsg) -> {
if (isEmpty(request.getParameter(param))) {
errors.add(errorMsg);
}
});
```
### 4. 使用 `Stream` API(Java 8+)
```java
Map<String, String> paramRules = Stream.of(
new AbstractMap.SimpleEntry<>("phoneOrAccount", "账号不能为空"),
new AbstractMap.SimpleEntry<>("accountType", "账号类型不能为空"),
new AbstractMap.SimpleEntry<>("deviceCode", "机器码不能为空"),
new AbstractMap.SimpleEntry<>("code", "图形验证码不能为空"),
new AbstractMap.SimpleEntry<>("verifyCode", "短信验证码不能为空")
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
```
### 5. 使用 Guava 库(第三方)
```java
// 需要添加 Guava 依赖
Map<String, String> paramRules = ImmutableMap.<String, String>builder()
.put("phoneOrAccount", "账号不能为空")
.put("accountType", "账号类型不能为空")
.put("deviceCode", "机器码不能为空")
.put("code", "图形验证码不能为空")
.put("verifyCode", "短信验证码不能为空")
.build();
```
### 完整示例(使用静态初始化块)
```java
private static final Map<String, String> PARAM_RULES;
static {
Map<String, String> map = new HashMap<>();
map.put("phoneOrAccount", "账号不能为空");
map.put("accountType", "账号类型不能为空");
map.put("deviceCode", "机器码不能为空");
map.put("code", "图形验证码不能为空");
map.put("verifyCode", "短信验证码不能为空");
PARAM_RULES = Collections.unmodifiableMap(map);
}
private void validateParameters(HttpServletRequest request) {
List<String> errors = new ArrayList<>();
PARAM_RULES.forEach((param, errorMsg) -> {
if (request.getParameter(param) == null || request.getParameter(param).isEmpty()) {
errors.add(errorMsg);
}
});
if (!errors.isEmpty()) {
throw new BadCredentialsException(String.join("; ", errors));
}
}
```
### 注意事项:
1. `Collections.unmodifiableMap()` 创建的映射是**不可修改**的,任何修改操作都会抛出 `UnsupportedOperationException`[^2]
2. 静态初始化方式只需初始化一次,性能更优
3. 使用匿名内部类方式会创建额外类,可能影响性能
4. 第三方库如 Guava 提供更丰富的不可变集合功能
这些方法都兼容 Java 8,其中静态初始化块方式是最推荐的做法,既保证了不可变性,又避免了重复创建映射对象的开销。
阅读全文
相关推荐










