Java 8,Lambda: 通过lambda替换匿名内部类

36

我有一个包含以下内容的类:

List roles = ldapTemplate.search(baseDn, replaceFilter, sc,
            new AttributesMapper() {
                public Object mapFromAttributes(Attributes attrs)
                        throws NamingException {
                    return attrs.get("cn").get();
                }
            });

IntelliJ建议我用lambda表达式替换匿名内部类,于是我尝试了:

List roles = ldapTemplate.search(
    baseDn, replaceFilter, sc,
    (Attributes a)  -> { return a.get("cn").get(); };
);

然而,我遇到了编译错误:

Error:(46, 50) java: incompatible types: inference variable T has incompatible bounds
    equality constraints: java.lang.String
    lower bounds: java.lang.Object

我找不到解决这个问题的方法。你有什么想法吗?


似乎需要将lambda的返回值转换为String类型。 - Bart
是的(属性a)->(字符串)a.get(“cn”)。get() 一切都很好。 但我不明白为什么在我的内部类中返回一个对象一切正常,而在lambda中却不是。 - user3934519
我不确定,但它一定与类型推断有关。我在我的机器上尝试了一个简单的测试,似乎可以在不进行强制转换的情况下工作。使用的Java版本是java version "1.8.0_05" Java(TM) SE Runtime Environment (build 1.8.0_05-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode) - Bart
4个回答

23
一个简单的Azure存储实体解析器接口及其实现方法:

一个简单的Azure存储实体解析器接口及其实现方法:

EntityResolver<String> orderNumberResolver = new EntityResolver<String>() {
    @Override
    public String resolve(String partitionKey, String rowKey, Date timeStamp,
        HashMap<String, EntityProperty> properties, String etag) {
      return properties.get("SomeColumnName").getValueAsString();
    }
  };

以上方法的Lambda将是:

 EntityResolver<String> orderNumberResolver = (
          partitionKey, rowKey, timeStamp, properties, etag
      ) -> properties.get("SomeColumnName").getValueAsString();

从上面的例子可以清楚地看出,Lambda 表达式足够智能,可以根据其匿名内部类来处理方法参数的类型,因此使重写方法的实现变得容易。希望这对你有帮助。


16

尝试一下这个(去掉多余的分号)

List roles = ldapTemplate.search(
    baseDn, replaceFilter, sc,
    (Attributes a)  -> { return a.get("cn").get(); }            
);

1
代码中有什么不同之处?啊,多了一个分号。你能把这个加到你的答案里吗? - tobias_k
1
'List roles = ldapTemplate.search(baseDn, replaceFilter, sc,(a) -> a.get("cn").get());' 即使这样也应该可以工作。 - snaikar

5

我强烈感觉你并没有在问题中发布准确的代码。和Bart一样,我无法通过您发布的代码复现错误。

然而,让我注意到的是你使用了原始类型。如果你的原始代码看起来像这样:

List<String> roles = ldapTemplate.search(baseDn, replaceFilter, sc,
    new AttributesMapper() {
        public Object mapFromAttributes(Attributes attrs)
                throws NamingException {
            return attrs.get("cn").get();
        }
    });

(请注意roles变量类型的更改) 如果您在没有类型参数实现AttributesMapper,那么只会得到一个原始类型警告,并且不会检查返回的Object是否有效作为List<String>的元素。

当将该代码转换为lambda表达式时,您不能再这样做了:

List<String> roles = ldapTemplate.search(baseDn, replaceFilter, sc,
  (Attributes a)  -> { return a.get("cn").get(); }            
);

现在,编译器将为您推断出类型AttributesMapper<String>并产生错误,因为您的lambda表达式返回Object而不是String,因此不能满足AttributesMapper<String>接口要求。

您可以通过插入类型转换来满足AttributesMapper<String>接口,也可以像您在问题中已经做过的那样,声明roles为原始类型List。然而,使用类型转换会更加简洁(而且应该是唯一不会产生编译器警告的方法):

List<String> roles = ldapTemplate.search(baseDn, replaceFilter, sc,
    a -> (String)a.get("cn").get());

我简化了表达式以弥补包含类型转换的问题,看起来好多了,不是吗?


0
以下代码展示了使用匿名内部类获取学生比较器。
private static final Comparator<StudentDto> studentScoreComparator = new Comparator<StudentDto>() {
            @Override
            public int compare(StudentDto s1, StudentDto s2) {
              if (s2.getScore() < s1.getScore()) {
                return 1;
              }
              return s2.getScore() == s1.getScore() ? 0 : 1;
            }
          };

使用Java8和Sonar建议将仅包含一个方法的匿名内部类转换成Lambda表达式可以改写为:

private static final Comparator<StudentDto> studentScoreComparator = (s1, s2) -> {
        if (s2.getScore() < s1.getScore()) {
          return 1;
        }
        return s2.getScore() == s1.getScore() ? 0 : 1;
      };

只有一个方法实现的匿名内部类可以视为函数式接口,并且可以轻松地处理为Lambda表达式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接