2

I'm trying in my code to use the following expression

    Map<String, String> headersMap =
             Collections.list(request.getHeaderNames())
        .stream()
        .collect(Collectors.toMap(
            name -> name,
            request::getHeader));

as shown here but Eclipse complains of type mismatch:

enter image description here

What's the correct way to accomplish this and why? Why does the one in the link not uses any casting?

UPDATES:

Considering the suggestion from @Eugene, the following monstrosity seems to make the compiler happier:

Map<String, String> headersMap = new HashMap<>();
 Collections.list(request.getHeaderNames())
 .forEach(x -> headersMap.put((String)x, (String)request.getHeader((String)x)));

Perhaps there are more succinct ways of expressing it in Java 8?

Simeon Leyzerzon
  • 18,658
  • 9
  • 54
  • 82
  • It's of type `org.eclipse.jetty.server.Request` – Simeon Leyzerzon Feb 13 '19 at 02:13
  • instead of `request::getHeader` it should really be `name -> request.getHeader()` – Eugene Feb 13 '19 at 02:41
  • Don't know. Just a sloppy first attempt to make it happy, perhaps there's a better way? – Simeon Leyzerzon Feb 13 '19 at 03:09
  • 1
    @Eugene no, `request::getHeader` is a correct short-hand for `name -> request.getHeader(name)` – Holger Feb 13 '19 at 09:03
  • Though not really clear from the question which classes are in use, and this question misses a crucial tag *android* which is where [the implementation of `getHeaderNames()`](https://android.googlesource.com/platform/external/jetty/+/master/src/java/org/eclipse/jetty/server/Request.java#610) is different. In my answer(previously), I'd just selected a class based on the first of google searches and that is where ended up interpreting the question incorrectly as well. – Naman Feb 13 '19 at 09:11
  • @nullpointer: It doesn't miss anything, because it's not `Android`, that link just happened to be the first one with the source of that library that came up in Google. As to which classes, I pointed the class in my comment: `org.eclipse.jetty.server.Request`, the rest of them are straight from Oracle's Java 8. – Simeon Leyzerzon Feb 13 '19 at 11:00
  • @SimeonLeyzerzon Bu in the answer you wrote, the links are inappropriate then. Have you followed them or not? Do make sure your question details out the source and classes in use as well. – Naman Feb 13 '19 at 11:19
  • @nullpointer all is fine, the link just happen to refer to the source of the class as I pointed above. And `Android` is appropriately not mentioned anywhere, as it's not used. I just noticed someone (you?) has put in among the tags in the meantime, so I'm removing that as misleading. – Simeon Leyzerzon Feb 13 '19 at 11:27
  • @SimeonLeyzerzon the "[***produces***](https://android.googlesource.com/platform/external/jetty/+/master/src/java/org/eclipse/jetty/server/Request.java#610)" mentioned link in your [Answer](https://stackoverflow.com/a/54662109/1746118) has a different implementation of `getHeaderNames` as compared to [`Request` class I'd mentioned in my answer.](https://www.eclipse.org/jetty/javadoc/9.4.12.v20180830/org/eclipse/jetty/server/Request.html) – Naman Feb 13 '19 at 11:29
  • @nullpointer I've updated it to a more representative link in my answer. Hopefully that alleviates your confusion. Thanks. – Simeon Leyzerzon Feb 13 '19 at 14:15

1 Answers1

3

Contrary to the @Eugene's assertions, what makes the difference here is that org.eclipse.jetty.server.Request.getHeaderNames() produces an Enumeration<Object> which, consequently, being fed into Collections.list() results in an ArrayList<Object> which is the reason Java compiler doesn't accept it.

Casting the end result to (Map<String, String>), as suggested by an IDE alleviates the complaint:

Map<String, String> headersMap =
 (Map<String, String>)
 Collections.list(request.getHeaderNames())
       .stream()
       .collect(Collectors.toMap(
                name -> name,
                request::getHeader));

Apparently the pages I mentioned (here and Servlet Filter: How to get all the headers from servletRequest?) are using the original method without eventually casting it to Map<String, String> are broken to start with (as suggested by @Eugene) or work for some other obscure reason.

Perhaps someone can shed light on that mystery.

UPDATES:

Upon further investigation as prompted by @StuartMarks's comment, indeed the culprit is the version of Jetty being used. Request.getHeaderNames() is implemented differently between the two.

Version 7.6.16 (which we happened to be using) uses raw-typed Enumeration, causing the havoc:

enter image description here

whereas Jetty ver. 9.4.7 uses a typed Enumeration:

enter image description here

It explains why the article I referenced in my original question as well as the other SO mention are both correct - they simply refer to a new implementation of that method. Again, thank you to @StuartMarks for pointing it out.

Simeon Leyzerzon
  • 18,658
  • 9
  • 54
  • 82
  • 2
    Looks like the Android version of `getheaderNames()` you linked to returns a *raw type* `Enumeration` and not `Enumeration`. But the effect is the same; it fouls up type inference, requiring you to add a cast in one place or another to get it to work. Newer versions of Jetty return `Enumeration` which makes the examples work correctly without casting. – Stuart Marks Feb 13 '19 at 06:18
  • 2
    Then, it would be cleaner to use `(Enumeration)request.getHeaderNames()`, to perform the entire stream operation without raw types, instead of casting the end result. – Holger Feb 13 '19 at 09:05
  • 1
    @StuartMarks Interesting, I'll look into upgrading our Jetty jars, perhaps that will work as you are saying. – Simeon Leyzerzon Feb 13 '19 at 10:53
  • 1
    @Holger: indeed, that is even cleaner and a more obvious option than casting of the end result and/or upgrading Jetty jars – Simeon Leyzerzon Feb 13 '19 at 11:40