36

I've got a class which contains the following:

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

IntelliJ tells me to replace the anonymous inner class with a lambda. So I tried:

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

However, I get a compilation error:

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

I can't find the solution to this problem. Do you have any ideas?

3
  • Looks like the return value of the lambda needs to be cast to a String. Commented Aug 12, 2014 at 17:31
  • yes (Attributes a) -> (String) a.get("cn").get() Everything is good. But I dont' understand why In my inner class I return an object and evverything is good and not in the lambda. Commented Aug 12, 2014 at 17:35
  • I'm not sure but it must have something to do with type inference. I just tried a simple test on my machine and it appears to work without casting. Used Java version is 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) Commented Aug 12, 2014 at 20:05

4 Answers 4

24

A simple azure storage Entity Resolver interface and it's implemented method:

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 of the above method will be :

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

It's clear from above example that lambda's are smart enough to handle the type of method parameters according to their anonymous inner class thus it makes the implementation of overridden method easy. Hope this will be helpful.

Sign up to request clarification or add additional context in comments.

Comments

16

Try this (removing extra semi colon)

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

2 Comments

Where's the difference to the code in the question? Ah, extra semicolon. Could you add this to your answer?
'List roles = ldapTemplate.search(baseDn, replaceFilter, sc,(a) -> a.get("cn").get());' even this should work
5

I have the strong feeling that you didn’t post the exact code in your question. Like Bart, I cannot reproduce the error with the code as you have posted.

However, what strikes me is your use of raw types. If your original code looked like this:

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

(note the changed type of the roles variable) you would get away with just a raw type warning as you implemented AttributesMapper without type arguments and there will be no check whether the returned Object will be valid as an element for a List<String>.

When converting that code into a lambda, you cannot get away with it anymore:

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

Now, the compiler will infer the type AttributesMapper<String> for you and produce an error because your lambda expression returns Object instead of String and thus does not fulfill the AttributesMapper<String> interface.

You can fix that by either inserting a type cast to fulfill the AttributesMapper<String> interface or by declaring roles with the raw type List as you already did in the question. However, using the type cast will be the cleaner way (and should be the only one which does not produce compiler warnings):

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

(I simplified the expression to compensate for the included type cast, looks much better, doesn’t it?)

Comments

0

Below code shows get student Comparator using annonymous inner class.

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;
            }
          };

Using Java8 amd Sonar suggestion to Anonymous inner classes containing only one method should become lambdas Can be converted as below.

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

Annonymous inner class which has only one method implementation can be consider as Functional Interface and can be easily treated as Lambda Expression.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.