Lambdas are just a way to create a type for a function. Keep in mind that a function has no "values" and hasn't been executed yet.
You can apply any Lambda to a set of values by calling it's apply method. It will then take the inputs (parameters to apply) and return the value as the result. A Lambda for a function that looks like this
(Coordinate x) -> { return new Distance(Math.sqrt(x.x * x.x + x.y * x.y)) }
would return the distance to the origin for a Coordinate assuming a Coordinate looked a little like
public class Coordinate {
public double x;
public double y;
}
and Distance looked a bit like like
public class Distance {
public Distance(double value) {
... whatever the implementation is ...
}
}
This function has a type conversion in it, it takes a Coordinate and returns a Distance. This means it fits the java "interface" of
Function<Coordinate, Distance>
and just by writing this Lambda
(Coordinate x) -> { return new Distance(Math.sqrt(x.x * x.x + x.y * x.y)) }
The Java compiler will generate some unnamed class of type Function<Coordinate, Distance> and instantiate an instance (create an object the class) to use in the context of the location of the Lambda.
Now if that lambda is within a method of a stream, such that the stream's parameter types are compatible, the stream will (in some manner) call apply on each value the stream is handling. Those values come from a Supplier which is basically an object that has a T get() method.
- Stream calls the Supplier's
get()method. - Stream has a method
.forEach( ... )containing a lambda that consumes theget()type. - Stream applies the value of
get()to the lambda inforEach()by passing it intoapply(value). - Stream collects the result of
apply(value) - Stream returns from the
.forEach(...)method a new stream with values typed to match the return value of the lambda in theforEach()method. - Eventually, these values are passed into a
Collectormethod which combines values into some sort of buffer. Sometimes a List, sometimes a single value.
Conveniences exist for various ways of simplifying the collectors. Conveniences exist for generating values without coding suppliers.
The lambda syntax itself is a convenience for not having to write an implementation of one of the "xxxFunction" interfaces, create an object of it, and pass it into the stream.
Predicates are what they call Functions that return boolean values. There are even more convenience functions that work with predicates.
So, if you don't have a collection of data points to process, you probably shouldn't be using lambdas. If you do have a collection of data points to process, then keep in mind that streams and lambdas provide a new not-quite-like-a-loop way of processing them. They are guaranteed to be applied to all values, but the order of their application is not necessarily in the strong ordering that a traditional loop would apply. With the right options, you can effectively split the input into multiple chunks (spliterator vs iterator) and process the data in parallel.
Now that you have a quick overview of Lambdas, Streams, and the Functional interfaces, you can see that
(loc, newloc) -> Math.pow((loc.getX()-newloc.getX()), 2) +
Math.pow(loc.getY()-newloc.getY(), 2)+Math.pow(loc.getZ()-newloc.getZ(),
2));
wouldn't "do" anything, because at best it describes this
public class MyFunction implements Function<Location, Location, Double> {
Double apply(Location first, Location second) {
return Math.pow((first.getX()-second.getX()), 2)
+ Math.pow(first.getY()-second.getY(), 2)
+ Math.pow(first.getZ()-second.getZ(), 2)
}
}
MyFunction myFunc = new MyFunction();
Which has the following problem"
- It's a coding error as it's only creating the facility to transform locations, and never using it.
Using the facility would look like
double result = myFunc.apply(loc, newloc);
Now, the very astute readers will mention auto-boxing, but in reality the compiler would choose the ToDoubleBiFunction type, probably side-stepping some of the possible auto-boxing issues. I just didn't want to write the example up in the non-generic manner, as again, the primitive functional types are a convenience (and optimization) of the general "all object" description above.
How do you use a lambda expresion with complex math in java - Stack Overflow
[Java] ELI5 Lambda expressions
Introduction to Lambda Expressions in Java
High level overview of Lambda Expressions in Java 8
Personally, I find it easier to think of lambdas as just syntactic sugar for interfaces with a single abstract method. It's not clear from the post when they can be used (e.g. not for abstract classes). Also, converting a for-loop to use forEach doesn't require a lambda. It's much cleaner with lambdas, but I could see someone getting confused as to when lambdas can/should be used.
More on reddit.comVideos
Lambdas are just a way to create a type for a function. Keep in mind that a function has no "values" and hasn't been executed yet.
You can apply any Lambda to a set of values by calling it's apply method. It will then take the inputs (parameters to apply) and return the value as the result. A Lambda for a function that looks like this
(Coordinate x) -> { return new Distance(Math.sqrt(x.x * x.x + x.y * x.y)) }
would return the distance to the origin for a Coordinate assuming a Coordinate looked a little like
public class Coordinate {
public double x;
public double y;
}
and Distance looked a bit like like
public class Distance {
public Distance(double value) {
... whatever the implementation is ...
}
}
This function has a type conversion in it, it takes a Coordinate and returns a Distance. This means it fits the java "interface" of
Function<Coordinate, Distance>
and just by writing this Lambda
(Coordinate x) -> { return new Distance(Math.sqrt(x.x * x.x + x.y * x.y)) }
The Java compiler will generate some unnamed class of type Function<Coordinate, Distance> and instantiate an instance (create an object the class) to use in the context of the location of the Lambda.
Now if that lambda is within a method of a stream, such that the stream's parameter types are compatible, the stream will (in some manner) call apply on each value the stream is handling. Those values come from a Supplier which is basically an object that has a T get() method.
- Stream calls the Supplier's
get()method. - Stream has a method
.forEach( ... )containing a lambda that consumes theget()type. - Stream applies the value of
get()to the lambda inforEach()by passing it intoapply(value). - Stream collects the result of
apply(value) - Stream returns from the
.forEach(...)method a new stream with values typed to match the return value of the lambda in theforEach()method. - Eventually, these values are passed into a
Collectormethod which combines values into some sort of buffer. Sometimes a List, sometimes a single value.
Conveniences exist for various ways of simplifying the collectors. Conveniences exist for generating values without coding suppliers.
The lambda syntax itself is a convenience for not having to write an implementation of one of the "xxxFunction" interfaces, create an object of it, and pass it into the stream.
Predicates are what they call Functions that return boolean values. There are even more convenience functions that work with predicates.
So, if you don't have a collection of data points to process, you probably shouldn't be using lambdas. If you do have a collection of data points to process, then keep in mind that streams and lambdas provide a new not-quite-like-a-loop way of processing them. They are guaranteed to be applied to all values, but the order of their application is not necessarily in the strong ordering that a traditional loop would apply. With the right options, you can effectively split the input into multiple chunks (spliterator vs iterator) and process the data in parallel.
Now that you have a quick overview of Lambdas, Streams, and the Functional interfaces, you can see that
(loc, newloc) -> Math.pow((loc.getX()-newloc.getX()), 2) +
Math.pow(loc.getY()-newloc.getY(), 2)+Math.pow(loc.getZ()-newloc.getZ(),
2));
wouldn't "do" anything, because at best it describes this
public class MyFunction implements Function<Location, Location, Double> {
Double apply(Location first, Location second) {
return Math.pow((first.getX()-second.getX()), 2)
+ Math.pow(first.getY()-second.getY(), 2)
+ Math.pow(first.getZ()-second.getZ(), 2)
}
}
MyFunction myFunc = new MyFunction();
Which has the following problem"
- It's a coding error as it's only creating the facility to transform locations, and never using it.
Using the facility would look like
double result = myFunc.apply(loc, newloc);
Now, the very astute readers will mention auto-boxing, but in reality the compiler would choose the ToDoubleBiFunction type, probably side-stepping some of the possible auto-boxing issues. I just didn't want to write the example up in the non-generic manner, as again, the primitive functional types are a convenience (and optimization) of the general "all object" description above.
Lambda expressions are used to generate anonymous functions. They can be used where a @FunctionalInterface is expected.
Read more about them here: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
If you'd like your code to work you can assign your lambda to a BiFunction variable and then execute it passing in the loc and newloc.
public class mathprob {
public static void main(String[] args) {
Location _loc = player.getlocation();
Location _newloc = player.getlocation();
BiFunction<Location, Location, Double> lambdaExpression = (loc, newloc) -> {
return Math.pow((loc.getX()-newloc.getX()), 2) +
Math.pow(loc.getY()-newloc.getY(), 2)+Math.pow(loc.getZ()-newloc.getZ(),
2);
};
double result = lambdaExpression.apply(_loc, _newloc);
}
}
Here is an example of how the equivalent method declaration would look like instead of using a lambda:
public class mathprob {
public static void main(String[] args) {
Location loc = player.getlocation();
Location newloc = player.getlocation();
double result = calculate(loc, newloc);
}
public static double calculate(Location loc, Location newloc) {
return
Math.pow((loc.getX() - newloc.getX()), 2) +
Math.pow(loc.getY() - newloc.getY(), 2) +
Math.pow(loc.getZ() - newloc.getZ(), 2);
}
}