when learning Flux (reactive-core) in java, I meet following questions about Function.
This is Flux.zip() method signature:
public static <I, O> Flux<O> zip(
final Function<? super Object[], ? extends O> combinator,
Publisher<?extends I>... sources) {
return zip(combinator, Queues.XS_BUFFER_SIZE, sources);
}
And when I try to invoke this method:
Flux<User> userFluxFromStringFlux(Flux<String> usernameFlux, Flux<String> firstnameFlux, Flux<String> lastnameFlux) {
// predefined function taking object[] and returns User
Function<Object[], User> function = array -> new User(array[0].toString(),array[1].toString(), array[2].toString());
// it is working without error
Flux.zip(function, usernameFlux, firstnameFlux, lastnameFlux);
// this is also working without error
Flux.zip(array -> {
return new User(array[0].toString(),array[1].toString(), array[2].toString());
}, usernameFlux, firstnameFlux, lastnameFlux);
// but this has error with array[0] "Array type expected; found: 'org.reactivestreams.subscriber<capture<? super java.lang.object>>'"
Flux.zip(array -> new User(array[0].toString(),array[1].toString(), array[2].toString()), usernameFlux, firstnameFlux, lastnameFlux);
return null;
}
The third way which using anonymous function, but IDEA reports that there is a error :
Array type expected; found: 'org.reactivestreams.subscriber>.
I wonder why predefined Function and anonymous function with explict return is working but anonymous function?
I appreciate your help.
Not a compiler expert, but I think it has to do with the java compiler seeing an ambiguity with the short form lambda: is what you are passing an inline Publisher (since it is a functional interface) or a Function?
This confusion is made possible because the short form doesn't have an explicit return statement: in the case of the Publisher option it would mean you create a User to immediately be garbaged collected, but that's not the sort of things the compiler will forbid you to do.
So the target type of the lambda is assumed to be Publisher, and thus array is inferred to be a Subscriber. But then the array index operator is used on it, which surely must be wrong.
On the other hand, putting in the brackets { } removes that ambiguity by having an explicit return type that seem to be used in the inference. To the compiler you can no longer be representing a Publisher, so the next candidate (Function) is used.
Another way of removing the ambiguity is to show the compiler that the array is... an array:
Flux.zip((Object[] array) -> new User(array[0].toString(),array[1].toString(), array[2].toString())
, usernameFlux, firstnameFlux, lastnameFlux);
Related
I was looking through the java language spec and saw this way of writing a creator:
Creator:
NonWildcardTypeArguments CreatedName ClassCreatorRest
In plain code:
new <Integer> String("1");
// or
new <String,Integer> Integer(5);
//or
new <Constructor,String,Integer> ArrayList<String>(5);
In every example I can think of the class list is not useful as far as I can tell. Can you please give me an example when the typelist after the new keyword serves a purpose.
This feature is invalid unless you run into the extremely exotic java syntactical constructor of a type-varred constructor. Essentially, you should never be doing this. It's there for language completeness and probably should never have been added to java.
You can put type param definitions on classes, this is the most common mode. For example, ArrayList has a type variable; it is declared as follows:
class ArrayList<E> implements List<E> { ... }
The first <E> declares that there is some type E with no bounds. It is then used immediately (the first <E> and the second <E> look identical, but one declares the type var, the other is a type var usage, and thus these are quite different. So far so good.
But you can also declare them directly on a method. For example:
public static <E> coalesce(E... items) {
for (E item : items) if (item != null) return item;
return item;
}
This method can be invoked with as many object refs as you please and will return the first one that isn't null, and the invocation's type will be the common denominator amongst all the parameters. In other words, this would compile:
String x = coalesce(null, someString, "defaultValue");
Whereas if we had written it:
public static Object coalesce(Object... items) {
for (Object item : items) if (item != null) return item;
return item;
}
Then, whilst coalesce(null, someStringRef, "default") would obviously neccessarily return a String, the compiler doesn't know that and thus String x = coalesce(..) would NOT compile. You'd have to inject a cast. The point of generics is to not have to do that.
And now to the answer to your question
This same feature (on-method type params) is available on constructors:
public class Example<T> {
public <A> Example(A first, A second) {
}
}
is legal java. It is extremely difficult to attempt to think of a situation where you'd want on-constructor generics. Constructors have no return type, and the whole point is that the typeparam is needed just for the constructor and ceases all relevance once you're done with it (if it is relevant to the instance itself, you'd put it on the type, just like <E> in the example above). Hence why you see it effectively never in java code. Nevertheless, if you were to write it, and you don't want javac to infer the generics but instead want to be explicit about it, the syntax you are reading about is how you'd do it:
new <String>Example<Integer>("a", "b");
Would invoke that constructor and force A to be String and T to be Integer.
There are boatloads of weird and confusing constructs in the JLS that nobody ever uses. Hence it's not generally a good idea to just read through the JLS, you'll learn something but it is a highly inefficient way of learning things.
I have the following assignment:
Design a class whose objects have the methods:
setValue which, given a String and an integer value, associates the value with the noun of the variable
getValue which returns the integer value associated with a given String
execute which invokes in sequence a given list of lambda expressions, where each one takes an instance of the class, return nothing, and act on the class through the methods setValue and getValue.
So for the example, after the following:
MyClass instance = new MyClass();
instance.execute(List.of(e -> e.setValue("x", 1),
e -> e.setValue("y", 2)));
instance should contain the values 1 for "x" and 2 for "y".
Here is what I've done so far, I think it's alright:
public class MyClass {
private Map<String,Integer> map;
public int getValue(String name) {return map.get(name);}
public void setValue(String name, int value) {map.put(name, value);}
I am not seeing the way to go for execute. I know I need a functional interface for those lambda expressions, but then I can't solve errors shown by Eclipse when writing instance.execute(List.of(...)) , e.g. "The method of(Object, Object) in the type List is not applicable for the arguments ((<no type> e) -> {}, (<no type> e) -> {}).
How can I make it work?
You need to supply a list of Consumer<MyClass>. A Consumer is a functional interface with a single abstract method called accept which performs the action on the the given argument. Try this.
public void execute(List<Consumer<MyClass>> list) {
list.forEach(cons -> cons.accept(this));
}
The problem with List::of is that you have to either use Java 9 or you can simply do this.
public void execute(Consumer<MyClass> ...list) {
Stream.of(list).forEach(cons -> cons.accept(this));
}
You have it backwards: If you want to call the method for a number of values, those values are the stream. Presuming you have a Map<String, Integer> that represents your "x": 1 dataset, you can do this:
valuesMap.entrySet().stream()
.forEach(entry -> instance.setValue(entry.getKey(), entry.getValue());
Given your problem statement, though, it looks like you're being asked to do something closer to the Visitor pattern. In this case, you need an interface for the lambdas to implement, and there's a built-in one: Consumer<MyClass>. You should get familiar with this one as well as Supplier and Function, since they're very common.
If you declare your execute method to take a list of these, then you can use a for loop or a stream to process them, or use the convenient built-in forEach method:
public void execute(List<Consumer<MyClass>> consumers) {
consumers.forEach(consumer -> consumer.accept(this));
}
I am confused by the following code
class LambdaTest {
public static void main(String[] args) {
Consumer<String> lambda1 = s -> {};
Function<String, String> lambda2 = s -> s;
Consumer<String> lambda3 = LambdaTest::consume; // but s -> s doesn't work!
Function<String, String> lambda4 = LambdaTest::consume;
}
static String consume(String s) { return s;}
}
I would have expected the assignment of lambda3 to fail as my consume method does not match the accept method in the Consumer Interface - the return types are different, String vs. void.
Moreover, I always thought that there is a one-to-one relationship between Lambda expressions and method references but this is clearly not the case as my example shows.
Could somebody explain to me what is happening here?
As Brian Goetz pointed out in a comment, the basis for the design decision was to allow adapting a method to a functional interface the same way you can call the method, i.e. you can call every value returning method and ignore the returned value.
When it comes to lambda expressions, things get a bit more complicated. There are two forms of lambda expressions, (args) -> expression and (args) -> { statements* }.
Whether the second form is void compatible, depends on the question whether no code path attempts to return a value, e.g. () -> { return ""; } is not void compatible, but expression compatible, whereas () -> {} or () -> { return; } are void compatible. Note that () -> { for(;;); } and () -> { throw new RuntimeException(); } are both, void compatible and value compatible, as they don’t complete normally and there’s no return statement.
The form (arg) -> expression is value compatible if the expression evaluates to a value. But there are also expressions, which are statements at the same time. These expressions may have a side effect and therefore can be written as stand-alone statement for producing the side effect only, ignoring the produced result. Similarly, the form (arg) -> expression can be void compatible, if the expression is also a statement.
An expression of the form s -> s can’t be void compatible as s is not a statement, i.e. you can’t write s -> { s; } either. On the other hand s -> s.toString() can be void compatible, because method invocations are statements. Similarly, s -> i++ can be void compatible as increments can be used as a statement, so s -> { i++; } is valid too. Of course, i has to be a field for this to work, not a local variable.
The Java Language Specification §14.8. Expression Statements lists all expressions which may be used as statements. Besides the already mentioned method invocations and increment/ decrement operators, it names assignments and class instance creation expressions, so s -> foo=s and s -> new WhatEver(s) are void compatible too.
As a side note, the form (arg) -> methodReturningVoid(arg) is the only expression form that is not value compatible.
consume(String) method matches Consumer<String> interface, because it consumes a String - the fact that it returns a value is irrelevant, as - in this case - it is simply ignored. (Because the Consumer interface does not expect any return value at all).
It must have been a design choice and basically a utility: imagine how many methods would have to be refactored or duplicated to match needs of functional interfaces like Consumer or even the very common Runnable. (Note that you can pass any method that consumes no parameters as a Runnable to an Executor, for example.)
Even methods like java.util.List#add(Object) return a value: boolean. Being unable to pass such method references just because that they return something (that is mostly irrelevant in many cases) would be rather annoying.
I have a stream of files that I want to filter based on the ending of the file name:
public Stream<File> getFiles(String ending) throws IOException {
return Files.walk(this.path)
.filter(Files::isRegularFile)
.map(Path::toFile)
.filter(file -> file.getName().endsWith(ending));
}
While the lambda in the last line is not bad, I thought I could use method references there as well, like so:
.filter(File::getName.endsWith(ending));
Or alternatively wrapped in parentheses. However, this fails with The target type of this expression must be a functional interface
Can you explain why this doesn't work?
Can you explain why this doesn't work?
Method references are syntactical sugar for a lambda expression. For example, the method reference File::getName is the same as (File f) -> f.getName().
Lambda expressions are "method literals" for defining the implementation of a functional interface, such as Function, Predicate, Supplier, etc.
For the compiler to know what interface you are implementing, the lambda or method reference must have a target type:
// either assigned to a variable with =
Function<File, String> f = File::getName;
// or assigned to a method parameter by passing as an argument
// (the parameter to 'map' is a Function)
...stream().map(File::getName)...
or (unusually) cast to something:
((Function<File, String>) File::getName)
Assignment context, method invocation context, and cast context can all provide target types for lambdas or method references. (In all 3 of the above cases, the target type is Function<File, String>.)
What the compiler is telling you is that your method reference does not have a target type, so it doesn't know what to do with it.
File::getName is a method reference and String::endsWith is as well. However they cannot be chained together. You could create another method to do this
public static Predicate<File> fileEndsWith(final String ending) {
return file -> file.getName().endsWith(ending);
}
and then use it
.filter(MyClass.fileEndsWith(ending))
This doesn't buy you much if you're not re-using it though.
A couple of helpers might assist in providing some semblance of what you wish for. Using the helpers below, you can replace your lambda with an expression containing method references, like this:
// since your predicate is on the result of a function call, use this to create a predicate on the result of a function
public static <A,B> Predicate<A> onResult(Function<A,B> extractor, Predicate<B> predicate){
return a -> predicate.test(extractor.apply(a));
}
// since your predicate involves an added parameter, use this to reduce the BiPredicate to a Predicate with one less parameter
public static <T,U> Predicate<T> withParam(BiPredicate<T,U> pred, U param){
return t -> pred.test(t,param);
}
public Stream<File> getFiles(String ending) throws IOException {
return Files.walk(Paths.get("."))
.filter(Files::isRegularFile)
.map(Path::toFile)
.filter(onResult(File::getName, withParam(String::endsWith, ending)));
}
Previous question
I have the following code:
ArrayList<Object> list = new ArrayList<Object>();
list.add("StringType");
list.add(5);
list.add(new RandomClass());
List<Class<?>> classes = new ArrayList<>();
classes.add(String.class);
classes.add(int.class);
classes.add(RandomClass.class);
for (int i = 0; i < list.size(); i++) {
if (classes.get(i).isInstance(list.get(i))) {
...
}
}
if (isvalid)
mymethod(...);
public void mymethod(String string, int num, RandomClass randomClass){ }
Now I'm trying to cast the object into the right type with a method using a string argument.
Instead of:
mymethod( (String) list.get(0), (int) list.get(1), (RandomClass) list.get(2) );
I would like to reuse the definition created above for the cast.
mymethod( ( define.get(0) ) list.get(0), .... );
I've also tried using the Class.cast(obj) but of course it returns a type '?' which again defeats the purpose of casting it again using (String).
What is type safety?
In computer science, type safety is the extent to which a programming
language discourages or prevents type errors.
If code is type safe, then the compiler can verify, at compile time, that all the types are correct:
String getName() {
return "name";
}
The compiler knows that "name" must be a String so it can verify that this code will never throw a type error.
Once you do something like:
int getNumber() {
(int) number;
}
The need to explicitly cast to int tells you that this code has an error condition, namely when number is not of type int or a type that is assignable to int.
How does it affect you?
Your code:
define.get(0).cast(list.get(0))
You want the return type of this statement to be of the type of get(0). But the compiler has no way of knowing, at compile time, what define.get(0) returns. This is inidcated to you by the return type.
You have a List<Class<?>>, i.e. a List of a class of "I don't care what type". You then use a member of this List to cast a member of your other List - the only result can be an "I don't care what type".
You can hack around this with:
<T> T get(final int i) {
return (T) define.get(i).cast(list.get(i));
}
This will happily compile:
final String thing = get(0);
As will:
final int thing = get(0);
i.e. all that you have done is to endogenise the cast. The error condition still exists.
define.get(0).cast(list.get(0)) would attempt to cast list.get(0) to the required type.
In order to be able to select the appropriate method, the compiler needs to know at compile time what the types of the arguments are. Or at least a general category such as List<?> etc.
This is needed to support overloading of methods. There can be many methods with the same name, but with different parameter types.
Since you are asking the VM to call a method when it can't determine which exact method you want to call, because it doesn't know at compile time what the types of your parameters are, what you ask cannot be done in Java.
Here is the relevant section from the Java Language Specification.
What it says is that the system selects at compile time which method signature to use, and then, at run time, the particular implementation of that method signature that's correct for the given instance.
You don't actually need to store object's class separately
list.get(0).getClass()
will get you the class of the stored object and then you can use what #Eran suggested
and
list.get(0).getClass().getName()
will get you the String name of your class