In our code, we use JWKset
from the nimbus-jose-jwt
library in a method that creates a map that we use for stuff. As you can see in the return, we use a stream that calls map on a list of RSAkey
, and then we create a List
from that, which is used to create a JWKSet
. We use collect(Collections.toList())
and that works, but when I tried to change this to use toList()
which our SonarQube suggested we should, it does not work.
The compiler error is:
Cannot resolve constructor 'JWKSet(List)
I also get the suggestion to 'cast argument to JWK'. But RSAKey
is a class that extends the abstract class JWK
if you look at the documentation https://github.com/felx/nimbus-jose-jwt/blob/master/src/main/java/com/nimbusds/jose/jwk/RSAKey.java
I do not see how the two method calls, which create the same type of output, do not work the same here.
final List<RSAKey> keys = Collections.singletonList(identityToRSAKey(certificate));
// Evidence the methold calls create the same type of output
List<RSAKey> list = keys.stream().map(RSAKey::toPublicJWK).toList();
List<RSAKey> collect = keys.stream().map(RSAKey::toPublicJWK).collect(Collectors.toList());
//This works
JWKSet mySet = new JWKSet(keys
.stream()
.map(RSAKey::toPublicJWK)
.collect(Collectors.toList()));
//This does not work
JWKSet mySet2 = new JWKSet(keys
.stream()
.map(RSAKey::toPublicJWK)
.toList() );
// Where we actually use the code
return new JWKSet(keys
.stream()
.map(RSAKey::toPublicJWK)
.collect(Collectors.toList() ))
.toPublicJWKSet()
.toJSONObject();
Constructor in JWKSet
that we try to invoke
public JWKSet(List<JWK> keys) {
this(keys, Collections.emptyMap());
}
The compiler error Cannot resolve constructor 'JWKSet(List<RSAKey>)'
is caused by the different signatures of collect()
and toList()
.
<R,A> R collect(Collector<? super T,A,R> collector)
List<T> toList()
The terminal operation collect()
accepts a Collector
, which is simply a mutable reduction operation, that starts from a type T
(in your case, RSAKey
), and returns a container R
of accumulated transformed results.
accumulates input elements into a mutable result container, optionally transforming the accumulated result into a final representation after all input elements have been processed
When you assign the result of the stream to List<RSAKey> collect
, no transformation is performed on the RSAKey
elements. The end result R
basically corresponds to a list of the supplied elements (List<T>
, in your case List<RSAKey>
). However, when the same stream appears as an argument for JWKSet
's constructor, the compiler tries to find a matching signature, and the only one available is JWKSet(List<JWK>)
. Therefore, every RSAKey
element of the stream is transformed (upcast) to JWK
, returning List<JWK>
.
On the other hand, toList()
simply returns a List
of the elements streamed. Therefore, the list you're obtaining is List<RSAKey>
, and not List<JWK>
. This is because toPublicJWK()
return type is RSAKey
(not JWK
), and since List<RSAKey>
is not a subtype of List<JWK>
(even though RSAKey
is a subtype of JWK
), you're receiving a compiler error when providing the stream to JWKSet
's constructor. For more details about subtyping with generics, I recommend this article by Angelika Langer.
In your case, I would just stick with collect()
, but if you really want (or need) to use toList()
, you can simply cast each element of the stream to JWK
.
JWKSet mySet = new JWKSet(keys
.stream()
.map(RSAKey::toPublicJWK) // I've left this invocation, because I don't know if it's really necessary to what you're doing
.map(JWK.class::cast)
.toList());