The following code produces an error for me in Eclipse Luna and prevents from running.
jdbcTemplate.query( query, r -> {
Folder folder = new Folder();
folder.setId(r.getLong("id"));
folder.setFolderName(r.getString("folder_name"));
folder.setFullPath(r.getString("full_path"));
folders.add(folder);
}, folderId);
return folders;
}
The error occurs at jdbcTemplate.query
The method query(String, ResultSetExtractor<Object>, Object[]) is ambiguous for the type JdbcTemplate
Error does not occur when I package file as jar and run.
Thanks for any guidance or suggestions.
There are three query methods in JdbcTemplate, as follows:
query(String, ResultSetExtractor<T>);
query(String, RowCallbackHandler);
query(String, RowMapper<T>);
In all three, the second argument can be implemented as a lambda, and the two that have a generic <T> parameter will return something. Eclipse is complaining that it can't tell the difference between the first and the third method you are referring to, so you need to specify the type of the functional interface that the lambda is replacing. Additionally, as Jack Ammo says, you need to return an object from the lambda to satisfy the interface. Don't add the returned object to your list of folders inside the lambda, either. So, you'd be looking at something like this:
Folder folder = jdbcTemplate.query( query, (ResultSetExtractor<Folder>) r -> {
Folder folder = new Folder();
folder.setId(r.getLong("id"));
folder.setFolderName(r.getString("folder_name"));
folder.setFullPath(r.getString("full_path"));
return folder;
}, folderId);
folders.add(folder);
return folders;
according to the javadoc for ResultSetExtractor, the method you're implementing with your lambda expression is Object extractData(ResultSet rs). extractData returns an Object so your lambda also has to return an Object in order to properly implement it.
Related
I am injecting a kotlin class into my java code. The kotlin class has two methods with nearly identical signatures:
fun <R> isFluxAuthorizedFor(
strings: List<StringRequest>,
then: (responses: List<StringResult>) -> Flux<R>
): Flux<R>
and
fun <R> isFluxAuthorizedFor(
string: StringRequest,
then: (per: StringResult) -> Flux<R>
): Flux<R> {
The kotlin class supports this overloading just fine.
However, I'm having a devil of a time getting my IDE to use the correct method. I have a method which matches the signature of the former:
private Flux<AuthorizedStrings> collectResults(List<StringResult> responses)
{
//not yet implemented
return null;
}
And yet, when I try and call the injected class' method, I get compilation errors:
List<StringRequest> allStrings = new ArrayList<StringRequest>();
Flux<UserReadAuthorizations> test = authCheck.isFluxAuthorizedFor(allStrings, (it) -> this.collectResults(it) );
The IDE makes two suggestions:
"change type of 'it' to 'List<StringResult>'"
"change method 'collectResults(List<StringResult>)' to 'collectResults(StringResult)'"
both of which imply that Java (or at least the compiler) can't figure out that I'm trying to call the other method. Is this a problem trying to integrate Java 8 and Kotlin? A quirk of the IDE? (I'm using Spring Tool Suite) Some silly user error that I've not yet been able to rubber-duck through?
I played with your code and found that IntelliJ tripped over the type of the lambda as a whole. I had to cast it -> this.collectResults(it) to the type Kotlin was expecting:
List<StringRequest> allStrings = new ArrayList<>();
Flux<UserReadAuthorizations> test = authCheck.isFluxAuthorizedFor(
allStrings,
(Function1<List<StringResult>, Flux<AuthorizedStrings>>) (it -> this.collectResults(it))
);
The signature of your methods themselves was not an issue. Hope this helps you in STS as well.
I have a collection that contains a list of errors. I wanted to group these by a key (UUID UserId). For this I have copied the code from this answer:
https://stackoverflow.com/a/30202075/4045364
Collection<FilterError> filterErrors = new ArrayList<FilterError>();
// ... some filterErrors get added to the collection ...
return filterErrors.stream().collect(Collectors.groupingBy(w -> w.getUserId()));
Sonar Lint gives me the following error:
Replace this lambda with a method reference. ->
What I have tried:
Based on these question: SONAR: Replace this lambda with a method reference and Runable Interface : Replace this lambda with a method reference. (sonar.java.source not set. Assuming 8 or greater.)
filterErrors.stream().collect(Collectors.groupingBy(this::getUserId()));
Based on this question: Replace this lambda with method reference 'Objects::nonNull'
filterErrors.stream().collect(Collectors.groupingBy(UUID::getUserId()));
Both give the error:
The target type of this expression must be a functional interface
Is there a way I can resolve this SonarLint issue?
You need to use the class name of the object being targeted by the stream.
Example:
List<String> list = ...;
list.stream().collect(Collectors.groupingBy(String::toUpperCase));
so in your case:
FilterError::getUserId
In my case previously it was like this -
whitelist0.stream().filter(whitelistEntry -> !whitelistEntry.isEmpty()).map(s -> WhitelistEntry.of(s)).collect(Collectors.toList()));
As I need to pass a value to the function, so I did the following to replace the lambda with method reference -
whitelist0.stream().filter(whitelistEntry -> !whitelistEntry.isEmpty()).map(WhitelistEntry :: of).collect(Collectors.toList()));
I'm using Wicket 6/Java 8 and am adding some simple classes that make use of the lambda functionality from Java 8 (I know the later versions of Wicket have lambda support but we can't upgrade right now). I'm creating a LambdaModel that is a bit like the PropertyModel, which I'm hoping will remove the need to hardcode Strings representing the nested path to the property.
To start, I'm making a simple readonly version. Ive made Serializable versions of the Function interface to create the following:
public class LambdaModelUtils {
public static <X,R> IModel<R> ofNested( IModel<X> target, SerializableFunction<?,?>... path ) {
// creates a model that works through each function in the path in turn
}
}
My implementation works well, but the only problem is that calling this method the 'efficient' way causes compile errors:
IModel<Parent> parentModel = ...
IModel<String> model = LambdaModelUtils.ofNested( parentModel,
Parent::getChild, Child::getName ); // Compile time error
The only way I can find to call the method is by the following:
SerializableFunction<Parent,Child> path0 = Parent::getChild;
SerializableFunction<Child,String> path1 = Child::getName;
IModel<String> model = LambdaModelUtils.ofNested( parentModel,
path0, path1 ); // works
This is a bit clumsy - is there a better way?
Ive looked here but this doesnt seem to work either:
List<SerializableFunction> path = Arrays.asList( Parent::getChild, Child::getName );
Thanks
If you're using these functions to get to a nested property, but don't really use the intermediate results, I'd advice you to just use a lambda expression:
public static <X,R> IModel<R> ofNested(IModel<X> target, SerializableFunction<X, R> path)
IModel<Parent> parentModel = ...
IModel<String> model = LambdaModelUtils.ofNested(parentModel, p -> p.getChild().getName());
This works since the target type of the lambda is now known, instead of the generic SerializedFunction<?, ?>, you get SerialiedFunction<X, R> where X = Parent and R = String.
I tried something similar to your code. Casting the method references to the functional interface type solves the compilation error:
IModel<String> model =
LambdaModelUtils.ofNested(parentModel,
(SerializableFunction<Parent,Child>) Parent::getChild,
(SerializableFunction<Child,String>) Child::getName);
Not the prettiest solution, but at least it saves you the need to declare the path0 and path1 variables.
I am currently experimenting in Spring MVC, and right now I'm trying to create a class of constants to populate a drop down list. The enum method is fine, but in my controller I get an error
"The method values() is undefined for the type String.
List<Job> jobList = new ArrayList<job>(
Arrays.asList(job.values()));
mav.addObject("jobList", jobList);
The error appears directly underneath .values() I have imported java.util.Enumeration so it's not that. Any ideas what it could be?
You want the values of the enum class Job, not call the values method on the job variable which in your case seems to be of type String.
Try:
List<Job> jobList = Arrays.asList(Job.values());
In Java 6, imagine I have the following method signature:
public void makeSandwich(Bread slice1, Bread slice2, List<Filling> fillings, boolean mustard)
I would like to know, at runtime, the value that was passed on to slice2 or any other parameter, the important bit here is that I want to get the value by parameter name.
I know how to get the list of parameter types with getParameterTypes or getGenericParameterTypes.
Ideally I would like to get a list of parameter names instead of types. Is there a way to do so?
Parameter names are available if you have told the compiler to include them (compile with debug information). Spring has ParameterNameDiscoverer which can help you obtain the names. The default implementation uses asm ClassReader to do so.
With javac you should include the -g argument to include debug information. With Eclipse I think it is there by default; it can be configured using the preferences: Java -> Compiler and then enable "Store information about method parameters (usable via reflection)" (see also this answer).
Some frameworks use this. For example spring-mvc has #RequestParam which defaults to the param name, if resolvable. It also supports explicit naming - #RequestParam("foo") in case no debug information is provided.
I have found another solution after marking this question as answered. The solution is Paranamer.
Example:
Method method = Foo.class.getMethod(...);
Paranamer paranamer = new CachingParanamer();
String[] parameterNames = paranamer.lookupParameterNames(method) // throws ParameterNamesNotFoundException if not found
// or ...
parameterNames = paranamer.lookupParameterNames(method, false) // will return null if not found
Since Java 1.8, this can be done as long as the parameter names are in the class files. Using javac this is done passing the -parameters flag. From the javac help
-parameters Generate metadata for reflection on method parameters
From IDEs you will need to look at the compiler settings.
If the parameter names are in the class files then here is an example of doing this
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class ParameterNamesExamples {
public static void main(String[] args) throws Exception {
Method theDoSomethingMethod = ExampleClass.class.getMethods()[0];
// Now loop through the parameters printing the names
for(Parameter parameter : theDoSomethingMethod.getParameters()) {
System.out.println(parameter.getName());
}
}
private class ExampleClass {
public void doSomething(String myFirstParameter, String mySecondParameter) {
// No-op
}
}
}
The output will depend on if the parameter names are in the class files. If they are the output is:
myFirstParameter
mySecondParameter
If not the output is:
arg0
arg1
More information on this from Oracle can be found at Obtaining Names of Method Parameters
In addition to this answer:
"Parameter names are available if you have told the compiler to include them"
If you're using Eclipse go to project -> properties -> Java Compiler -> check "Store information about method parameters (usable via reflection)
In Java parameter names are not available via reflection.
This is not possible. Class files do not contains the argument names, as you can see with your IDE's autocompletion when the source is not available.
Therefore, the reflection API is not able to give out parameter names.
You can simply assign the value of the parameter to another value
Bread slice2;
public void makeSandwich(Bread slice1, Bread slice2, List<Filling> fillings, boolean mustard) {
this.slice2 = slice2;
System.out.println(this.slice2.getSomething());
}
Do you own the code of the method? You could annotate the parameters and pass names as arguments #Param("slice1"). Later you will be able to get the annotation and extract parameter name from it.