How to pass methods as parameters? [duplicate] - java

This question already has answers here:
Java Pass Method as Parameter
(17 answers)
How to pass a function as a parameter in Java? [duplicate]
(8 answers)
Closed 3 years ago.
I have a class with bunch of methods. In another class, I need to write a method, that handles the input values. To that method, I want to pass the method of the class that I want to call. How can we do that with Java after 1.8?
There are similar questions already, but those usually assume that we can use an interface with a single method, therefore can use lambda expressions, etc.
class MyClass {
public Object myToString(String a) {
return new String(a);
}
public Object myToString(String a, String b) {
return new String(a + ", " + b);
}
public Object mySum(int a) {
return new Integer(a);
}
public Object mySum(int a, int b) {
return new Integer(a + b);
}
}
class Test {
public Object handleInputs(MyClass myClass, MethodAsParameter theMethod, List<Object> inputs) {
if (type of inputs are Strings) {
myClass.myToString(inputs.get(0));
} else if (.....) {
myClass.mySum(inputs.get(0));
}
}
}

Since Java 8 you can use method reference. Method references can be assigned to Function<A, B> functional interface variables and their subclasses.
For example, method with such signature:
class Test {
public static int DoSomething(String s) {...}
}
can be assigned to a Function<String, Integer> variable like:
Function<String, Integer> method = Test::DoSomething;
and then called:
int result = method.apply("Hello!");
So with small improvements in your code, this is the way you can use your methods as method references and passed to other function as parameters.
class MyClass {
public static String myToString(String a, String b) {
return a + ", " + b;
}
//notice the boxing in here
public static int mySum(int a, int b) {
return a + b;
}
//not kind of an revolutionary function, just for demonstration
public static<T> T Invoke(BinaryOperator<T> bo, T o1, T o2) {
return bo.apply(o1, o2);
}
public static void main(String[] args) {
int sum = Invoke(MyClass::mySum, 10, 20);
String str = Invoke(MyClass::myToString, "a", "b");
System.out.println(sum);
System.out.println(str);
}
}

I think something like this is as far as you would get:
import java.util.List;
import java.util.Arrays;
import java.util.function.Function;
import java.util.function.BiFunction;
class MyClass {
public Object myToString(String a) {
return new String(a);
}
public Object myToString(String a, String b) {
return new String(a + ", " + b);
}
public Object mySum(int a) {
return Integer.valueOf(a);
}
public Object mySum(int a, int b) {
return Integer.valueOf(a + b);
}
}
public class MethodParams {
public static Object handleInputs(Function<Object,Object> method, List<Object> inputs) {
return method.apply(inputs.get(0));
}
public static Object handleInputs(BiFunction<Object,Object,Object> method, List<Object> inputs) {
return method.apply(inputs.get(0), inputs.get(1));
}
public static void main(String args[]) {
MyClass mc = new MyClass();
String str = (String)handleInputs((a) -> mc.myToString((String)a), Arrays.asList("string"));
System.out.println(str); // string
Integer sum = (Integer)handleInputs((a) -> mc.mySum((int)a), Arrays.asList(1));
System.out.println(sum); // 1
Integer sum2 = (Integer)handleInputs((a,b) -> mc.mySum((int)a, (int)b), Arrays.asList(1, 2));
System.out.println(sum2); // 3
}
}
Not very nice, but at least you have some leeway as to which method you want to use. Code as demonstrated here has lots of casts due to using Objects - using generic types as demonstrated by t2dohx is better way of doing this, but even further from your question.

Here is a simple example:
public class TestMain {
public static void main(String [] args) {
Long a = 15L, b = 20L;
Long sum = combineTwoNumbers(a, b, (p1, p2) -> p1 + p2);
Long product = combineTwoNumbers(a, b, (p1, p2) -> p1 * p2);
System.out.println("Sum is " + sum);
System.out.println("Product is " + product);
}
public static Long combineTwoNumbers(Long a, Long b, BiFunction <Long, Long, Long> combiner) {
return combiner.apply(a, b);
}
}
Here, the functional parameter is BiFunction, which takes two parameters in input and returns an output. Specifically, it takes two long numbers and produces a third one as a result. The name of the method is kept generic so that it can cover more instances of different functions that may take place. In our example we are passing a sum and a product function as you can see.

Related

How Can I call a diferent function for each element stream in Java 8? [duplicate]

This question already has answers here:
How to call a method stored in a HashMap? (Java) [duplicate]
(3 answers)
Closed 3 years ago.
I would like to do a similar approach above in Java 8. I'm pythonist, here an example of what I need to do, but in Python.
def function1(x):
return x * 1
def function2(x):
return x * 2
def function3(x):
return x * 3
status = {"BOOK": function1,
"ISSUING": function2,
"RETRYING": function3}
for k, v in status.items():
print("status {0} call function {1}, \
result: {2}".format(k, v, v(2)))
How have the same effect or approach in Java 8? Please?
After #Guilheme help me above [SOLVED], I got this similar approach:
import java.util.HashMap;
import java.util.Map;
public class ExampleCallDifferentFunction {
public static int multiply1(int x) {
return x * 1;
}
public static int multiply2(int x) {
return x * 2;
}
public static int multiply3(int x) {
return x * 3;
}
interface Function {
int function(int x);
}
private static Map<String, Function> createMap() {
Map<String,Function> myMap = new HashMap<String,Function>();
myMap.put("BOOK", (x) -> multiply1(x));
myMap.put("ISSUING", (x) -> multiply2(x));
myMap.put("RETRYING",(x) -> multiply3(x));
return myMap;
}
public static void main(String[] args) {
Map<String, Function> status = createMap();
int x = 2;
for (Map.Entry<String, Function> entry : status.entrySet()) {
Function f = entry.getValue();
System.out.printf("status %s call %s result: %d\n", entry.getKey(), entry.getValue(), f.function(2));
}
}
}
While you could use a map, it feels like an enum would be a more natural solution:
enum Status {
BOOK(x -> x * 1),
ISSUING(x -> x * 2),
RETRYING(x -> x * 3);
private final IntUnaryOperator operator;
private Status(IntUnaryOperator operator) {
this.operator = operator;
}
public int apply(int argument) {
return operator.apply(argument);
}
}
Then you can use valueOf to convert from String (this example uses Optional to catch the illegal value case):
Optional.ofNullable(Status.valueOf(status)).orElseThrow().apply(argument);
Or if you want to apply each value:
for (Status status: Status.values()) {
System.out.println(status + ":" + status.apply(2));
}
In general, if you know the set of objects at compile time then consider an enum. It is clear documentation for the reader of your code that this is a fixed list that won't change at runtime.
As a matter of interest, in the openjdk implementation of Java valueOf actually uses a map from String to the constant internally so this is no less efficient.
here is the code:
import java.util.HashMap;
import java.util.Map;
public class Main {
interface Function {
int function(int x);
}
public static void main(String[] args) {
Map<String, Function> status = new HashMap<>();
status.put("BOOK", (x) -> x);
status.put("ISSUING", (x) -> x * 2);
status.put("RETRYING", (x) -> x * 3);
for (Map.Entry<String, Function> entry : status.entrySet()) {
Function f = entry.getValue();
System.out.printf("status %s call %s result: %d\n", entry.getKey(), entry.getValue(), f.function(2));
}
}
}

Return an array and a variable together in main function

I have a function like that
Class Return_two{
public static void main(String args[]){
int b=0;// Declare a variable
int a []= new int[3];// Declare an array [both are return at the end of the user define function fun()]
Return_two r=new Return_two();
int result_store= r.fun(a,b);//where should I store the result meaning is it a normal variable or an array where I store the result?
}
public int [] fun (int[] array,int var)//may be this is not a good Return type to returning an array with a variable so what will be change in return type?
{
for(int counter=0;counter <array.length;counter++)
{ var=var+counter;
}
return( array,var);// Here how could I return this two value in main function?
}
}
Now, here lies my question. I want to return an array with a variable as I written above.But as I know one can return a array or a variable but not both. Or one can return one or more variable make those variable as a array element. But how can one return an array with an variable in main function?
If you want to create multiple values, wrap them in an object.
(I'm not able to come up with a meaningful name from what you have posted)
class Result {
private int[] a;
private int b;
public Result(int[] a, int b) {
this.a = a;
this.b = b;
}
//Getters for the instance variables
public int[] getA() {
return a;
}
public int getB() {
return b;
}
}
At the end of fun
return new Result(array, var);
Some best practices:
Don't declare variable names with same name as a parameter (a in fun)
In the above Result class, better to create copies on the array a to avoid mutations outside the class.
If possible, don't use arrays and use a List (this would give you a lot of flexibility)
EDIT:
Your caller will look like
Return_two r=new Return_two();
Result result = r.fun(a, b);
result.getA();//Do whatever you want to do with the array
result.getB();//Do whatever you want to do with that variable
With your current version of the (modified) code, why do you want to return the array since it is same as what you pass to the fun method? Returning only the computed var will work for you (and hence the return type can simply be int).
You can also achieve what you do in fun in one line
return (array.length * (array.length - 1)) / 2;
Wrap these properties into a object, say
Public class FunModel
{
public int[] a;
public int b;
}
then you can return an instance of `FunModel`.
Or
you can use `Tuples`
------------------
Futher Explanation
------------------
The return type here should be a model.
This model should have all that you want to return as properties.
You can return this model from your method.
public class FunModel
{
public int[] a;
public int b;
public FunModel(int[] a, int b) {
this.a = a;
this.b = b;
}
}
And the method should return a instance of this model.
public class ReturnTwo {
public static void main(String args[]){
int b=0;
int a []= new int[3];
ReturnTwo returnTwo = new ReturnTwo();
FunModel funModel = returnTwo.fun(a,b);
//other processing
}
public FunModel fun (int[] array,int tempVar)
{
FunModel temp = new FunModel(array,tempVar);
for(int counter=0;counter <array.length;counter++)
{
temp.b = temp.b + counter;
}
return temp;// you return the model with different properties
}
}

Scala -> Java , Pass function as a parameter to another function

I am exploring both scala and Java 1.8 but unable to find equivalent code in with java 1.8 lambda expressions.
Scala code:
object Ex1 extends App {
def single(x:Int):Int =x
def square(x:Int):Int = x * x
def cube(x:Int):Int = x*x*x
def sum(f:Int=>Int,a:Int,b:Int):Int=if (a>b) 0 else f(a) + sum(f,a+1,b)
def sumOfIntegers(a:Int,b:Int)=sum(single,a,b)
def sumOfSquares(a:Int,b:Int)=sum(square,a,b);
def sumOfCubes(a:Int,b:Int)=sum(cube,a,b);
println(sumOfIntegers(1,4));
println(sumOfSquares(1,4));
println(sumOfCubes(1,4));
}
output:
10
30
100
java:
public class Test1{
public int single(int x){
return x;
}
public int square(int x){
return x * x;
}
public int cube(int x){
return x * x * x;
}
// what's next? How to implement sum() method as shown in Scala?
// Stuck in definition of this method, Stirng s should be function type.
public int sum( Sring s , int a, int b){
// what will go here?
}
public int sumOfIntegers(int a, int b){
return sum("single<Function> how to pass?",a,b);
}
public int sumOfSquares(int a, int b){
return sum("square<Function> how to pass?",a,b);
}
public int sumOfCubes(int a, int b){
return sum("cube<Function> how to pass?",a.b);
}
}
Is it possible to achieve the same with JDK 1.8?
You are going to need to define the methods that you are going to use. The only one that comes with Java is the Function<X,Y> which (with Integer) can be used for single, square and cube and BiFunction<T,Y, R> which can be used for the three sumOfs.
interface Sum {
Integer apply(Function<Integer, Integer> func, int start, int end);
}
The single, square and cube could be either methods in the class (see cube or inline (see the other two). They are Fuctions. This function can then be passed to other methods and called with variableName.apply.
class Test
{
private static Integer sum(Function<Integer, Integer> func, int a, int b) {
if (a>b)
return 0;
else return func.apply(a) + sum(func,a+1,b);
}
private static Integer cube(Integer a) {
return a * a * a;
}
public static void main (String[] args) throws java.lang.Exception
{
Function<Integer,Integer> single = a -> a;
Function<Integer,Integer> square = a -> a * a;
Function<Integer,Integer> cube = Test::cube;
// You can not do the sum in-line without pain due to its recursive nature.
Sum sum = Test::sum;
BiFunction<Integer, Integer, Integer> sumOfIntegers = (a, b) -> sum.apply(single, a, b);
BiFunction<Integer, Integer, Integer> sumOfSquares = (a, b) -> sum(square, a, b); // You could just use the static method directly.
BiFunction<Integer, Integer, Integer> sumOfCubes = (a, b) -> sum(cube, a, b);
System.out.println(sumOfIntegers.apply(1,4));
System.out.println(sumOfSquares.apply(1,4));
System.out.println(sumOfCubes.apply(1,4));
}
}
Yes, it is possible:
public Integer sumSingle(Integer a) { return a; }
public int sum(Function<Integer, Integer> f, int a, int b)
{
... : f.apply(a+1); // or whatever
}
public int sumOfIntegers(int a, int b)
{
return sum(Test1::single, a, b);
}
The construct Class::method creates a Function object from the method. Or if you don't need to reuse it, you can directly pass an argument instead: (Integer a) -> a*a* ...
Function<Integer, Integer> single = x -> x;
Function<Integer, Integer> square = x -> x*x;
public int sum( Function<Integer, Integer> s , int a, int b){
if (a>b){
return 0;
} else {
s.apply(a) + sum(s,a+1,b)
}
}
In Java 8 you can use any functional interface, i.e. an interface with exactly one non-static method without a default implementation, in place of Scala's Function<N> types. There are quite a few such interfaces in the java.util.function package, for single-argument and two-argument functions; in particular for functions which take and return int you should use IntUnaryOperator, already mentioned in other people's comments. If you have more than two arguments, or two arguments of different types, define your own interface with one method. E.g. for functions of 3 arguments which take int, long and long and return a non-primitive:
interface Fun3ILLO<T> {
public T apply(int arg1, long arg2, long arg3);
}
(obviously you can choose the interface and method name you want).

sorting on the basis on a column not alphabetically

I have a bean called vulnerability. It is having a column "severity".
private String severity;
Severity can hold string value High,Medium and Low. Now whenever sorting of this bean on the basis of severity column is done it happens alphabetically i.e. High,Low and Medium. But i want the sorting to happen high,medium, low when descending and low, medium,high when ascending.
I was seeing comparator to make this custom sorting but it needs to cover lots of cases. Isn't their any other way?
You can (and should) use an enum - not a String nor a int:
enum Severity {
LOW, MEDIUM, HIGH;
}
Usage:
List<Severity> lst = new ArrayList<Severity>();
lst.add(Severity.MEDIUM);
lst.add(Severity.LOW);
lst.add(Severity.HIGH);
for (Severity s : lst)
System.out.println("s = " + s);
Collections.sort(lst);
System.out.println();
for (Severity s : lst)
System.out.println("s = " + s);
OUTPUT:
s = MEDIUM
s = LOW
s = HIGH
s = LOW
s = MEDIUM
s = HIGH
EDIT
Since the OP says he can't modify the usage of Strings, we can map the strings into a comparable values:
static Map<String, Integer> severities = new HashMap<String, Integer>();
static {
severities.put("LOW",1);
severities.put("MEDIUM",2);
severities.put("HIGH",3);
}
public static void main(String[] args) {
List<String> lst = new ArrayList<String>();
lst.add("MEDIUM");
lst.add("LOW");
lst.add("HIGH");
for (String s : lst)
System.out.println("s = " + s);
Collections.sort(lst, new Comparator<String>() {
public int compare(String a1, String a2) {
Integer v1 = severities.get(a1);
Integer v2 = severities.get(a2);
return v1.compareTo(v2);
}
});
System.out.println();
for (String s : lst)
System.out.println("s = " + s);
}
and if you want to order the items in descending order you can sort and then reverse:
Collections.sort(lst);
Collections.reverse(lst);
There is an implicit compareTo operator defined on enums, which takes their declaration order to mean "smaller than". No additional code is needed.
enum Severity { Low, Medium, High }
Low.compareTo(High); // returns -1
Medium.compareTo(Low); // returns 1
However, note that the names of the enum constants will be those printed by toString() (and therefore visible to users if you echo enums directly) - if you want to use different internal and external names, possibly to uphold code conventions (say, all-caps-constants), then you will need to add an enum constructor and override the enum's toString method to use the passed-in constructor attribute.
If you cannot use enums, and you cannot change your bean
Then build a Comparator for it:
public class SeverityComparator implements Comparator<String> {
private int direction;
public SeverityComparator(boolean reverse) {
this.direction = reverse ? -1 : 1;
}
private int severity(String s) {
if (s.equals("Low")) { // you really should have constants for the values...
return 0;
} else if (s.equals("Medium")) {
return 1;
} else if (s.equals("High")) {
return 2;
} else {
throw new IllegalArgumentException("Not a severity: " + s);
}
}
#Override
public int compareTo(String other) {
return direction * (severity(this) - severity(other));
}
}
Use as
Collections.sort(listOfSeverities, new SeverityComparator(false)); // ascending
Collections.sort(listOfSeverities, new SeverityComparator(true)); // descending
#alfasin answer is correct but i would suggest using guava's Ordering:
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import javax.annotation.Nullable;
import java.util.List;
public class SeveritySortTest {
private static final List<Severity> SEVERITY_LIST = ImmutableList.copyOf(Severity.values());
public static void main(String[] args) {
Ordering<Severity> severityOrdering = Ordering.natural().onResultOf(new Function<Severity, Integer>() {
#Nullable
#Override
public Integer apply(#Nullable Severity input) {
return input.getSeverity();
}
});
List<Severity> sortedAscending = severityOrdering.sortedCopy(SEVERITY_LIST);
List<Severity> sortedDescending = severityOrdering.reverse().sortedCopy(SEVERITY_LIST);
}
enum Severity {
LOW(1), MEDIUM(2), HIGH(3);
private int severity;
Severity(int s) {
severity = s;
}
int getSeverity() {
return severity;
}
}
}
Working Solution:
Collections.sort(recommendations, new Comparator() {
private int priority(String s) {
if (s.equalsIgnoreCase("Low")) {
return 1;
} else if (s.equalsIgnoreCase("Medium")) {
return 2;
} else if (s.equalsIgnoreCase("High")) {
return 3;
} else {
return 0;
}
}
#Override
public int compare(Recommendation o1, Recommendation o2) {
return -1 * (priority(o1.getPriority()) - priority(o2.getPriority()));
}
});
If you want the DB to do this through JPA/Hibernate you could create a sort expression based on a simple case statement, assuming your entity is called Case:
Expression exp = criteriaBuilder.selectCase(root.get(Case_.priority)).when("High", 1).when("Medium", 2).otherwise(3);
queryBuilder.orderBy(orderDir.isAscending() ? criteriaBuilder.asc(exp) : criteriaBuilder.desc(exp));
Using case statements in an order by clause isn't great for performance, but solves it. Works with Oracle.

Sorting an ArrayList, into object type and then alphabetically

I have defined an arrayList
Each of these objects have there own name etc.
I would like to sort them by type, so all watermelons are together and pears are together etc. then for them to be sorted alphabetically within each time.
Something using
public int compare(String[] first, String[] second) {
return first[1].compareTo(second[1]);
}
Also is it possible to just do this in an already created class, to avoid doing it in a new class.
Close, but you won't be able to pass String arrays as parameters to the compare method. You need to create a Comparator<Food> and the parameters will be Food references.
Then in the compare(Food f1, Food f2) method you'll need to compare the class, and if they're the same, then the name.
You are pretty close. You can do something like this (one-shot with anonymous implementation of Comparator interface so you don't need a new class):
Collections.sort(foodList, new Comparator<Food>() {
#Override
public int compare(Food food1, Food food2) {
int result = 0;
if(food1.getType().equals(food2.getType())) {
result = food1.getName().compareTo(food2.getName());
} else {
result = food1.getType().compareTo(food2.getType());
}
return result;
}
});
Another way is to have Food implement Comparable<T> and use the same logic for the compareTo(T o) implementation.
The comparison works by first checking to see if the types of the food are equal (assuming the food type has an applicable equals() method). If they are equal, the comparison needs to be done on the basis of their names. Otherwise, the comparison will be done based on the food type.
You can implement the Comparable interface in your Food implementations or subclasses. Though it is easy to write an implementation that works for your case, it is not as trivial as it seems to write a completely correct Comparable implementation, especially concerning inheritance. See Effective Java, item 12 for a good documentation of the requirements and of common pitfalls.
Your compare method name is wrong. It would be compareTo of Comparable interface.:
public int compareTo(Fruit second) {
return this.getWaterMelon().compareTo(second.getWaterMelon());
}
I would say that your compare() method will need to return weighed value for your type compareTo() difference + name compareTo() difference.
Something like
public int compare(Food food1, Food food2) {
int result = 0;
result = food1.getType().compareTo(food2.getType()) * someMultiplier + food1.getName().compareTo(food2.getName());
return result;
}
EDIT:
This is the usage that I was suggesting...
public class ComparatorTest {
static class ComaparableObject implements Comparable {
private Object value1;
private Object value2;
public ComaparableObject(Object value1, Object value2) {
this.value1 = value1;
this.value2 = value2;
}
#Override
public int compareTo(Object o) {
int multiplier = 65535;
ComaparableObject co = (ComaparableObject) o;
int result = (value1.hashCode() * multiplier + value2.hashCode()) - (co.value1.hashCode() * multiplier + co.value2.hashCode()) ;
return result;
}
public Object getValue1() {
return value1;
}
public void setValue1(Object value1) {
this.value1 = value1;
}
public Object getValue2() {
return value2;
}
public void setValue2(Object value2) {
this.value2 = value2;
}
public String toString() {
String result = "value1=" + value1 + ", " + "value2=" + value2;
return result;
}
}
public static void main(String[] args) {
ArrayList<ComaparableObject> al;
al = new ArrayList<ComaparableObject>();
ComaparableObject co;
int value1 = 2;
co = new ComaparableObject(value1, 3);
al.add(co);
co = new ComaparableObject(value1, 1);
al.add(co);
co = new ComaparableObject(value1, 2);
al.add(co);
value1 = 1;
co = new ComaparableObject(value1, 3);
al.add(co);
co = new ComaparableObject(value1, 1);
al.add(co);
co = new ComaparableObject(value1, 2);
al.add(co);
System.out.println("Before sort: " + al);
Collections.sort(al, new Comparator<ComaparableObject>() {
#Override
public int compare(ComaparableObject co1, ComaparableObject co2) {
int result;
result = co1.compareTo(co2);
return result;
}
});
System.out.println("After sort: " + al);
}
}

Categories

Resources