Java Lambdas: Sending method name as parameter - java

I've run into a problem in which my class contains several methods with a lot of duplicated code. The reason behind this is that each method traverses a list of entries and calls specific entry method.
In code...
The LowLevelClass class has the following structure:
public class LowLevelClass {
// constructor omitted
public boolean doSomethingA() {
// some non-duplicated code
return true;
}
public boolean doSomethingB() {
// some non-duplicated code
return true;
}
public boolean doSomethingC() {
// some non-duplicated code
return true;
}
}
The top level class contains a List of LowLevelClasses and has the same number of methods, but this time, with a lot of duplications:
public class HighLevelClass {
private List<LowLevelClass> classes = new ArrayList<>();
public HighLevelClass() {
this.classes.add(new LowLevelClass(/* params */));
this.classes.add(new LowLevelClass(/* params */));
this.classes.add(new LowLevelClass(/* params */));
}
public void doA() {
System.out.println("Doing ...");
for (LowLevelClass entry : classes) {
System.out.println("Doing something...");
entry.doSomethingA();
System.out.println("Done");
}
}
public void doB() {
System.out.println("Doing ...");
for (LowLevelClass entry : classes) {
System.out.println("Doing something...");
entry.doSomethingB();
System.out.println("Done");
}
}
public void doC() {
System.out.println("Doing ...");
for (LowLevelClass entry : classes) {
System.out.println("Doing something...");
entry.doSomethingC();
System.out.println("Done");
}
}
}
My goal is to have something in form of:
public class HighLevelClass {
private List<LowLevelClass> classes = new ArrayList<>();
public HighLevelClass() {
this.classes.add(new LowLevelClass());
this.classes.add(new LowLevelClass());
this.classes.add(new LowLevelClass());
}
public void doSomething(Lambda /* Functional interface*/ operation) {
System.out.println("Doing A");
for (LowLevelClass entry : classes) {
System.out.println("Doing something...");
entry.operation; // or something else...
System.out.println("Done");
}
}
public void doSomethingA() {
// my goal... and maybe in totally wrong direction is to send something in form of...
return doSomething(LowLevelClass::doSomethingA);
}
// etc
}
Can this be done in Java 8 with Lambdas? In other words, can I define the method to perform on each entry of the given list?
EDIT 1
The answers provided by Jorn Vernee and Joffrey are correct!
Ultimately, the solution was to use Predicate. (see EDIT 2 why I didn't use Consumer in the end...)
public class HighLevelClass {
private List<LowLevelClass> classes = new ArrayList<>();
public HighLevelClass() {
this.classes.add(new LowLevelClass());
this.classes.add(new LowLevelClass());
this.classes.add(new LowLevelClass());
}
public boolean doSomething(Predicate<LowLevelClass> function) {
System.out.println("Doing A");
for (LowLevelClass entry : classes) {
System.out.println("Doing something...");
boolean val = function.test(entry);
System.out.println("Done " + val);
}
return someEndVerdict;
}
public boolean doSomethingA() {
return doSomething(LowLevelClass::doSomethingA);
}
// etc
}
EDIT 2
My initial methods in HighLevelClass didn't contain boolean return type. That's the reason why I used Predicate (Predicate, as a contast to Consumer, returns boolean value which suited me better - and which I forgot to initially mention :((( )
Thanks for help and time!

You should not confuse the way you call a method, which may or may not involve a lambda, and the way you write a method, which involves finding the right argument types.
When you write a method, you need to focus on your arguments' types. If one of them is an object representing a function, what you need is to understand the appropriate signature that this function should match, and this will give you the functional interface you should put as type of your param.
In your case, you expect a function that takes 1 argument of type LowLevelClass and returns no value. You might be surprised by that, but you need to think of instance methods as functions that take an instance of the class (this) as an extra first argument (as opposed to static methods).
Therefore, the Consumer<LowLevelClass> interface is what you want:
public void doSomething(Consumer<LowLevelClass> operation) {
System.out.println("Doing A");
for (LowLevelClass entry : classes) {
System.out.println("Doing something...");
operation.accept(entry); // or something else...
System.out.println("Done");
}
}
public void doSomethingA() {
return doSomething(LowLevelClass::doSomethingA);
}

Related

Java reduce number of constructors

Suppose I have some class that has constructors, each of which has 2 params
for example I have 5 types, two of them can be passed as first param and three as second
Suppose the types are A,B,C,D,E. Basically I have something like this:
public SomeClass(A,C) {}
public SomeClass(A,D) {}
public SomeClass(A,E) {}
public SomeClass(B,C) {}
public SomeClass(B,D) {}
public SomeClass(B,E) {}
So as you can see there are all possible combinations of these types
and there aren't optional params, like in standard builder pattern.
What can I do to reduce number of constructors? Is there some pattern to help me?
EDIT
I can't use standard builder because if I supplied A then I can't supply B
and if I supplied C then I can't supply D or E
You can create a restricted builder pattern:
class MyBuilder {
public MyBuilder(A) { ... }
public MyBuilder(B) { ... }
public SomeClass build(C) { ... }
public SomeClass build(D) { ... }
public SomeClass build(E) { ... }
}
Example usage:
new MyBuilder(A).build(C)
If you have only 6 possible combinations, I would probably tolerate the complexity and duplication in the existing code. But if there are more possibilities and/or the code will be extended in the future, then I would recommend this pattern here.
You can extend this pattern to more arguments, but it gets uglier because you need more intermediate classes. For 3 arguments, you might implement something like:
class MyBuilder1 {
public MyBuilder1(A) { ... }
public MyBuilder1(B) { ... }
public MyBuilder2 build(C) { ... }
public MyBuilder2 build(D) { ... }
public MyBuilder2 build(E) { ... }
class MyBuilder2 {
public MyBuilder2(C) { ... }
public MyBuilder2(D) { ... }
public MyBuilder2(E) { ... }
public SomeClass build(F) { ... }
public SomeClass build(G) { ... }
}
}
SomeClass x = new MyBuilder1(B).build(D).build(F);
Unfortunately the intermediate stage needs duplication of methods and constructors for types C, D, E.
Create 2 interfaces, one for your first parameter, one for your second parameter. A and B implement the first one, C,D and E the second one.
A suggestion. Put the logic into your builder equivalent class. Add a method to check that the parameter combination is valid.
class MyBuilder {
int paramA;
bool paramAset = false;
long paramB
bool paramBset = false;
// etc for all parameters.
void setParamA(int newAval) {
if (checkParamA(newAval)) {
paramA = newVal;
paramAset = true;
}
}
void setParamB(long newBval) {
if (checkParamB(newBval)) {
paramB = newBval;
paramBset = true;
}
}
// etc. for all parameters.
// Parameter checker method.
bool checkValid() {
if (
paramAset && !paramBset && /* other params */ ||
!paramAset && paramBset && /* other params */ ||
// other allowed parameter combinations
) {
return true;
} else {
return false;
}
}
} // end class MyBuilder
Each parameter has its own value and a boolean to indicate whether it is set. The checker method only allows valid combinations of the booleans. Note that each parameter is specified as either set or not set in the checker to catch all invalid combinations. Your constructor for the main class needs to call the checker method and fail appropriately if the combination is invalid.

How do I pass a method as a parameter in Java 8?

I don't understand how to use lambdas to pass a method as a parameter.
Considering the following (not compiling) code, how can I complete it to get it work ?
public class DumbTest {
public class Stuff {
public String getA() {
return "a";
}
public String getB() {
return "b";
}
}
public String methodToPassA(Stuff stuff) {
return stuff.getA();
}
public String methodToPassB(Stuff stuff) {
return stuff.getB();
}
//MethodParameter is purely used to be comprehensive, nothing else...
public void operateListWith(List<Stuff> listStuff, MethodParameter method) {
for (Stuff stuff : listStuff) {
System.out.println(method(stuff));
}
}
public DumbTest() {
List<Stuff> listStuff = new ArrayList<>();
listStuff.add(new Stuff());
listStuff.add(new Stuff());
operateListWith(listStuff, methodToPassA);
operateListWith(listStuff, methodToPassB);
}
public static void main(String[] args) {
DumbTest l = new DumbTest();
}
}
Declare your method to accept a parameter of an existing functional interface type which matches your method signature:
public void operateListWith(List<Stuff> listStuff, Function<Stuff, String> method) {
for (Stuff stuff : listStuff) {
System.out.println(method.apply(stuff));
}
}
and call it as such:
operateListWith(listStuff, this::methodToPassA);
As a further insight, you don't need the indirection of methodToPassA:
operateListWith(listStuff, Stuff::getA);
Your MethodParameter should be some interface you define with a single method. This is referred to as a functional interface. You can then pass your methods in. A quick demonstration:
public interface Test{
void methodToPass(string stuff);
}
[...]
public class DumbTest{
//MethodParameter is purely used to be comprehensive, nothing else...
public void operateListWith(List<Stuff> listStuff, Test method) {
for (Stuff stuff : listStuff) {
System.out.println(method(stuff));
}
}
public DumbTest() {
List<Stuff> listStuff = new ArrayList<>();
//fill list
operateListWith(listStuff, methodToPassA);
operateListWith(listStuff, methodToPassB);
}
}
The definition of MethodParameter is missing from your source code. To be used with lambda expressions, it must be a functional interface, for example:
#FunctionalInterface
interface MethodParameter {
String apply(Stuff input);
}
(The #FunctionalInterface annotation is optional.)
To use the method, you have call the method from the interface:
System.out.println(method.apply(stuff));
And thirdly, a method reference always needs a context. In your case you have to do:
operateListWith(listStuff, this::methodToPassA);
operateListWith(listStuff, this::methodToPassB);
You need to use method references.
You don't need to create a method like operateListWith, that's sort of the whole idea. Instead, you can operate on each value using forEach by doing something like this:
listStuff.stream.forEach(object::methodToPassA);
For example:
public class StreamExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("Hello", "What's Up?", "GoodBye");
list.stream().forEach(System.out::println);
}
}
Output:
Hello
What's Up?
GoodBye
In your case, you can get the value inside Stuff using .map, and then operate on it using forEach, like this:
public class DumbTest {
public class Stuff {
public String getA() {
return "a";
}
public String getB() {
return "b";
}
}
public String methodToPassA(Stuff stuff) {
return stuff.getA();
}
public String methodToPassB(Stuff stuff) {
return stuff.getA();
}
public DumbTest() {
List<Stuff> listStuff = Arrays.asList(new Stuff(), new Stuff());
listStuff.stream()
.map(this::methodToPassA)
.forEach(System.out::println);
}
public static void main(String[] args) {
DumbTest l = new DumbTest();
}
}

Replacing if else statement with pattern

I have a if else statement which might grow in the near future.
public void decide(String someCondition){
if(someCondition.equals("conditionOne")){
//
someMethod("someParameter");
}else if(someCondition.equals("conditionTwo")){
//
someMethod("anotherParameter");
}
.
.
else{
someMethod("elseParameter");
}
}
Since, this is already looking messy, I think it would be better if I can apply any design patterns here. I looked into Strategy pattern but I am not sure if that will reduce if else condition here. Any suggestions?
This is a classic Replace Condition dispatcher with Command in the Refactoring to Patterns book.
Basically you make a Command object for each of the blocks of code in your old if/else group and then make a Map of those commands where the keys are your condition Strings
interface Handler{
void handle( myObject o);
}
Map<String, Handler> commandMap = new HashMap<>();
//feel free to factor these out to their own class or
//if using Java 8 use the new Lambda syntax
commandMap.put("conditionOne", new Handler(){
void handle(MyObject o){
//get desired parameters from MyObject and do stuff
}
});
...
Then instead of your if/else code it is instead:
commandMap.get(someCondition).handle(this);
Now if you need to later add new commands, you just add to the hash.
If you want to handle a default case, you can use the Null Object pattern to handle the case where a condition isn't in the Map.
Handler defaultHandler = ...
if(commandMap.containsKey(someCondition)){
commandMap.get(someCondition).handle(this);
}else{
defaultHandler.handle(this);
}
Let's assume that we have such code (which is the same as yours):
public void decide(String someCondition) {
if(someCondition.equals("conditionOne")) {
someMethod("someParameter");
}
else if(someCondition.equals("conditionTwo")) {
someMethod("anotherParameter");
}
else {
someMethod("elseParameter");
}
}
Assuming that you don't want to refactor other parts of the application and you don't want to change method signature there are possible ways in which it could be refactored:
Warning - You should use generic versions of mentioned patterns.
I showed non generic ones because it is easier to read them.
Strategy + Factory Method
We can use Strategy and Factory Method patterns. We also take advantage of polymorphism.
private final StrategyConditionFactory strategyConditionFactory = new StrategyConditionFactory();
public void decide(String someCondition) {
Strategy strategy = strategyConditionFactory.getStrategy(someCondition)
.orElseThrow(() -> new IllegalArgumentException("Wrong condition"));
strategy.apply();
}
It would be better to design it in a way that else condition is included in the factory, and developer calls it on purpose. In such case we throw exception when condition is not meet. Alternatively we could write it exactly as it was in question. If you want so instead of .orElseThrow(() -> new IllegalArgumentException("Wrong condition")); put .orElse(new ElseStrategy());
StrategyConditionFactory (factory method):
public class StrategyConditionFactory {
private Map<String, Strategy> conditions = new HashMap<>();
public StrategyConditionFactory() {
conditions.put("conditionOne", new ConditionOneStrategy());
conditions.put("conditionTwo", new ConditionTwoStrategy());
//It is better to call else condition on purpose than to have it in the conditional method
conditions.put("conditionElse", new ElseStrategy());
//...
}
public Optional<Strategy> getStrategy(String condition) {
return Optional.ofNullable(conditions.get(condition));
}
}
Strategy interface:
public interface Strategy {
void apply();
}
Implementations:
public class ConditionOneStrategy implements Strategy {
#Override
public void apply() {
//someMethod("someParameter");
}
}
public class ConditionTwoStrategy implements Strategy {
#Override
public void apply() {
//someMethod("anotherParameter")
}
}
public class ElseStrategy implements Strategy {
#Override
public void apply() {
//someMethod("elseParameter")
}
}
Usage (simplified):
public void strategyFactoryApp() {
//...
decide("conditionOne");
decide("conditionTwo");
decide("conditionElse");
//...
}
Strategy + Factory Method - this particular case (where only parameter changes)
We can use the fact that in this case we always call the same method, only parameter changes
We change our base strategy interface to abstract class with getParameter() method and we make new implementations of this abstract class. Other code remains the same.
public abstract class Strategy {
public abstract String getParameter();
public void apply() {
someMethod(getParameter());
}
private void someMethod(String parameter) {
//someAction
}
}
Implementations:
public class CondtionOneStrategy extends Strategy {
#Override
public String getParameter() {
return "someParameter";
}
}
public class CondtionTwoStrategy extends Strategy {
#Override
public String getParameter() {
return "anotherParameter";
}
}
public class ElseStrategy extends Strategy {
#Override
public String getParameter() {
return "elseParameter";
}
}
Enum + enum kinda "factory"
We might use Enum to implement strategy and instead of factory method we can use valueOf() from enum.
public void decide(String someCondition) {
ConditionEnum conditionEnum = ConditionEnum.valueOf(someCondition);
conditionEnum.apply();
}
Condition enum:
public enum ConditionEnum {
CONDITION_ONE {
#Override
public void apply() {
//someMethod("someParameter");
}
},
CONDITION_TWO {
#Override
public void apply() {
//someMethod("anotherParameter");
}
},
CONDITION_ELSE {
#Override
public void apply() {
//someMethod("elseParameter");
}
};
//...more conditions
public abstract void apply();
}
Usage (simplified):
public void enumFactoryApp() {
//...
decide("CONDITION_ONE");
decide("CONDITION_TWO");
decide("CONDITION_ELSE");
//...
}
Notice that you will get IllegalArgumentException when enum type has no constant with the specified name.
Command + Factory
The difference between strategy and command is that command holds also state, so if you have for example compute(int a, int b, String someCondition) and you want to refactor it with strategy including it's signature change you can reduce it to compute(int a, int b, ComputeStrategy computeStrategy) with command you can reduce it to one argument compute(ComputeCommand computeCommand). In this case we also take advantage of polymorphism similarly to strategy pattern case.
CommandConditionFactory commandConditionFactory = new CommandConditionFactory();
public void decide(String someCondition) {
Command command = commandConditionFactory.getCommand(someCondition)
.orElseThrow(() -> new IllegalArgumentException("Wrong condition"));
command.apply();
}
It would be better to design it in a way that else condition is included in the factory, and developer calls it on purpose. In such case we throw exception when condition is not meet. Alternatively we could write it exactly as it was in question. If you want so instead of .orElseThrow(() -> new IllegalArgumentException("Wrong condition")); put .orElse(new ElseCommand());
CommandConditionFactory (factory method):
public class CommandConditionFactory {
private Map<String, Command> conditions = new HashMap<>();
public CommandConditionFactory() {
conditions.put("conditionOne", new ConditionOneCommand("someParameter"));
conditions.put("conditionTwo", new ConditionTwoCommand("anotherParameter"));
//It is better to call else condition on purpose than to have it in the conditional method
conditions.put("conditionElse", new ElseCommand("elseParameter"));
//...
}
public Optional<Command> getCommand(String condition) {
return Optional.ofNullable(conditions.get(condition));
}
}
Command interface:
public interface Command {
void apply();
}
Implementations (there is some redundancy, but It is there to show how command should look in more general case where instead of someMethod() we have three different methods):
public class ConditionOneCommand implements Command {
private final String parameter;
public ConditionOneCommand(String parameter) {
this.parameter = parameter;
}
#Override
public void apply() {
//someMethod(parameter);
}
}
public class ConditionTwoCommand implements Command {
private final String parameter;
public ConditionTwoCommand(String parameter) {
this.parameter = parameter;
}
#Override
public void apply() {
//someMethod(parameter);
}
}
public class ElseCommand implements Command {
private final String parameter;
public ElseCommand(String parameter) {
this.parameter = parameter;
}
#Override
public void apply() {
//someMethod(parameter);
}
}
Usage (simplified):
public void commandFactoryApp() {
//...
decide("conditionOne");
decide("conditionTwo");
decide("conditionElse");
//...
}
Command + Factory - This particular case.
This in fact isn't a real command pattern just a derivative. It takes advantage of the fact that in this case we are always calling the same method someMethod(parameter) and only the parameter changes.
Abstract class:
public abstract class Command {
abstract void apply();
protected void someMethod(String parameter) {
//someAction
}
}
Implementation (the same for all 3 conditional cases):
public class CommandImpl extends Command {
private final String parameter;
public CommandImpl (String parameter) {
this.parameter = parameter;
}
#Override
public void apply(){
someMethod(parameter);
}
}
Factory, please notice that there is only one command implementation, only parameter changes:
public class CommandConditionFactory {
Map<String, Command> conditions = new HashMap<>();
public CommandConditionFactory() {
conditions.put("conditionOne", new CommandImpl("someParameter"));
conditions.put("conditionTwo", new CommandImpl("anotherParameter"));
//It is better to call else condition on purpose than to have it in the conditional method
conditions.put("conditionElse", new CommandImpl("elseParameter"));
//...
}
public Optional<Command> getCommand(String condition) {
return Optional.ofNullable(conditions.get(condition));
}
}
Nested if's
Note that even if you have nested ifs sometimes it is possible to refactor them and use one of the mentioned techniques.
Lets say that we have following code:
public void decide2(String someCondition, String nestedCondition) {
if(someCondition.equals("conditionOne")) {
if(nestedCondition.equals("nestedConditionOne")){
someLogic1();
}
else if(nestedCondition.equals("nestedConditionTwo")){
someLogic2();
}
}
else if(someCondition.equals("conditionTwo")) {
if(nestedCondition.equals("nestedConditionThree")){
someLogic3();
}
else if(nestedCondition.equals("nestedConditionFour")){
someLogic4();
}
}
}
You could refactor it using mathematical logic rules:
public void decide2(String someCondition, String nestedCondition) {
if(someCondition.equals("conditionOne")
&& nestedCondition.equals("nestedConditionOne")) {
someLogic1();
}
else if(someCondition.equals("conditionOne")
&& nestedCondition.equals("nestedConditionTwo")) {
someLogic2();
}
else if(someCondition.equals("conditionTwo")
&& nestedCondition.equals("nestedConditionThree")) {
someLogic3();
}
else if(someCondition.equals("conditionTwo")
&& nestedCondition.equals("nestedConditionFour")) {
someLogic4();
}
}
and then you can use strategy, enum or command. You just have a pair of Strings <String, String> instead of single String.
Decision Tables
When you have nested ifs that couldn't be refactored as mentioned you can implement your own decision tables or use some ready to go decision tables solution. I won't give the implementation there.
Rules Engine
When you have nested ifs that couldn't be refactored as mentioned you can also implement your own simple rules engine. You should use it only if you have many nested ifs, otherwise it is triumph of form over content.
For very complicated Business Logic there are professional Rule Engines like Drools.
I won't give the implementation there.
One more thing
In the example that you gave there is a high possibility that someone introduced these ifs, but they are totally redundant. And we can check it by trying to refactor decide method signature to make it take some other argument and to refactor surrounding code that is calling our method. By doing so we are getting rid of our Factory Method. There are examples that present how the code might look when it occurs that these ifs were redundant.
Strategy
Decide method:
public void decide(Strategy strategy) {
strategy.apply();
}
Usage (simplified):
public void strategyApp() {
//...
decide(new ConditionOneStrategy());
decide(new ConditionTwoStrategy());
decide(new ElseStrategy());
//...
}
Enum
Decide method:
public void decide(ConditionEnum conditionEnum) {
conditionEnum.apply();
}
Usage (simplified):
public void enumApp() {
//...
decide(ConditionEnum.CONDITION_ONE);
decide(ConditionEnum.CONDITION_TWO);
decide(ConditionEnum.CONDITION_ELSE);
//...
}
Command
Decide method:
public void decide(Command command) {
command.apply();
}
Usage (simplified):
public void commandApp() {
//...
decide(new ConditionOneCommand("someParameter"));
decide(new ConditionTwoCommand("anotherParameter"));
decide(new ElseCommand("elseParameter"));
//...
}
In fact it is quite specific case, there are cases in which for example we have to use simple type like String, because it comes from the external system or condition is based on integer from input so we can't refactor the code so easily.
The general recommendation by Martin Fowler is to
Replace Conditional with Polymorphism.
In terms of design patterns this would often be the Strategy Pattern
Replace Conditional Logic with Strategy.
If you have a small, finite set of conditions, I recommend to use an enum to implement the Strategy Pattern (provide an abstract method in the enum and override it for each constant).
public enum SomeCondition{
CONDITION_ONE{
public void someMethod(MyClass myClass){
//...
}
},
CONDITION_TWO{
public void someMethod(MyClass myClass){
}
}
public abstract void someMethod(MyClass myClass);
}
public class MyClass{
//...
public void decide(SomeCondition someCondition){
someCondition.someMethod(this);
}
}
If it's really just a parameter you want to pick, then you could define the enum like this instead:
public enum SomeCondition{
CONDITION_ONE("parameterOne"),
CONDITION_TWO("parameterTwo");
private final String parameter;
private SomeCondition(String parameter){
this.parameter = parameter;
}
public String getParameter(){
return parameter;
}
}
public class MyClass{
//...
public void decide(SomeCondition someCondition){
someMethod(someCondition.getParameter());
}
}
Another way to solve the current problem is to use Factory Pattern. This provides functionality to extract a factory method that returns an object of a given type and performs the operation based on the concrete object behavior.
public interface Operation {
String process(String a, String b);
}
The method takes two string as input and returns the result.
public class Concatenation implements Operation {
#Override
public String process(String a, String b) {
return a.concat(b);
}
}
public class Join implements Operation {
#Override
public String process(String a, String b) {
return String.join(", ", a, b);
}
}
And then we should define a factory class which returns instances of Operation based on the given operator:
public class OperatorFactory {
static Map<String, Operation> operationMap = new HashMap<>();
static {
operationMap.put("concatenation", new Concatenation());
operationMap.put("join", new Join());
// more operators
}
public static Optional<Operation> getOperation(String operator) {
return Optional.ofNullable(operationMap.get(operator));
}
}
And now we can use it:
public class SomeServiceClass {
public String processUsingFactory(String a, String b, String operationName) {
Operation operation = OperatorFactory
.getOperation(operationName)
.orElseThrow(() -> new IllegalArgumentException("Invalid Operation"));
return operation.process(a, b);
}
}
I guess you must have already considered it, but if you are using JDK 7 or above, you can switch on strings. That way your code can look cleaner than a bunch of if-else statements.

java run method on every element of arraylist

I have a large number of methods that need to be applied to each member of a given arrayList. The problem is that I'm at compile time uncertain which methods need to be applied to the arrayList and applying all of them each time (with a check) would be to costly (realtime application). So I'm now looking for a way to give the list a number of methods (a la function programming) and have them run. I know I could create special classes where each class uses only a uses a single method on each of it's members but there are roughly 50 methods so that would quickly escalate into 50 different classes. Which would add a lot of unnecessary complexity to the project. Is there another way?
So what I'm looking for is something like this:
public void runMethode(Function f,ArrayList<ObjectWithF> al){
for(ObjectWithF o:al){
o.f();
}
}
Does such a thing exist in java? Or am I going to have to either call all 50 methods or create 50 different classes? Or would a wrapper pattern work here?
In Java 8, this functionality is provided via the Stream#forEach(Consumer) method in a very convenient form.
If you don't have Java 8 yet, then you can create an interface, and create instances of anonymous classes implementing this interface. This is a bit more verbose, but still better than 50 named classes.
It may be beneficial to design this interface in a way that is structurally equal to the Consumer interface that is used in Java 8, so that it may simply be changed to use the Java 8 version once you do the update.
This example shows a comparison of both approaches:
import java.util.ArrayList;
import java.util.List;
class Person
{
void sayHello()
{
System.out.println("Hello from "+this);
}
void sayGoodbye()
{
System.out.println("Goodbye from "+this);
}
}
// This interface already exists as java.util.function.Consumer in Java 8
interface Consumer<T>
{
void accept(T t);
}
public class ForEachTest
{
public static void main(String[] args)
{
List<Person> persons = new ArrayList<Person>();
for (int i=0; i<5; i++)
{
persons.add(new Person());
}
runJava8(persons);
runJava7(persons);
}
private static void runJava8(List<Person> persons)
{
persons.stream().forEach(Person::sayHello);
persons.stream().forEach(Person::sayGoodbye);
}
private static void runJava7(List<Person> persons)
{
runMethodJava7(persons, new Consumer<Person>()
{
#Override
public void accept(Person person)
{
person.sayHello();
}
});
runMethodJava7(persons, new Consumer<Person>()
{
#Override
public void accept(Person person)
{
person.sayGoodbye();
}
});
}
public static void runMethodJava7(
List<Person> persons, Consumer<? super Person> consumer)
{
for(Person person : persons)
{
consumer.accept(person);
}
}
}
reflect can help.
see the code blow :
String obj = "abc";
String methodName = "toString";
try {
Method method = obj.getClass().getMethod("toString");
System.out.println(method.invoke(obj));
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
we have a obj, and given a method, we can run this method on that object.
And, you can also consider define a interface, and all your objects in the list implements the interface. and iterate the list, call those methods which defined in the interface.
As of Java 8, use may use Consumer class:
public void runMethod(Consumer<ObjectWithF> consumer, ArrayList<ObjectWithF> al) {
for (ObjectWithF o : al) {
consumer.accept(o);
}
}
...
// for example
runMethod (o -> System.out.println (o), listOfObjectsWithF);
// or
runMethod (System.out::println, listOfObjectsWithF);
// or even
listOfObjectsWithF.forEach(System.out::println);
If it's not Java8, you may create this interface yourself and realize it for every method:
interface Consumer {
public void apply(ObjectWithF o);
}
public void runMethod(Consumer consumer, ArrayList<ObjectWithF> al) {
for (ObjectWithF o : al) {
consumer.apply(o);
}
}
...
runMethod(new Consumer() {
public void apply(ObjectWithF o) {
//for example
System.out.println (o.toString());
}
}, listOfObjectsWithF);

Wrap/Proxy Boilerplate from Methods

I'm not a Java specialist and wondering if I could wrap Methods that are only different in their middle to stop having boilerplatecode like this:
public boolean storeAnimals(Cage cage, Collection<Anmial> animals, IConfiguration configuration) {
checkPrerequisite(cage);
String cagePath = cage.getPath();
AnimalStorage animalStore = AnimalStorage.openFile(cage, configuration);
//***/
do_sth_very_special
//***/
animalStore.closeFile();
return true;
}
public Collection<Anmial> getRedAnimals(Cage cage, IConfiguration configuration) {
checkPrerequisite(cage);
String cagePath = cage.getPath();
File animalStore = AnimalStorage.openFile(cage, configuration);
//***/
do_sth_very_special
//***/
animalStore.closeFile();
return result;
}
Since java doesn't present a kind of closure, you could use an interface for it. In this case you could do something like:
public Collection<Anmial> commonMethod(Cage cage, IConfiguration configuration, Runnable runnable) {
checkPrerequisite(cage);
String cagePath = cage.getPath();
File animalStore = AnimalStorage.openFile(cage, configuration);
//***/
runnable.run();
//***/
animalStore.closeFile();
return result;
}
and you method would be something like:
public boolean storeAnimals(Cage cage, Collection<Anmial> animals, IConfiguration configuration) {
commonMethod(cage, animals, configuration, new Runnable() {
public void run() {
System.out.println("something special");
}
});
return true;
}
of course the Runnable interface was taken just to exemplify the idea, you could implement an interface for your needs. If you need that the inner code accesses some variables defined outside you can pass it as parameters, and if the outside code needs to access some variables defined inside you could return them from the invocation.
You could define a generic interface like:
public interface Executable<ReturnType, ParameterType> {
ReturnType execute(ParameterType parameter);
}
In case you need more parameters to execute the code, you could build a class containing all needed fields for the code execution. The same could be applied to the result.
This would be the idea to return a list of string:
new Executable<List<String>, Void>() {
public List<String> execute(Void void) {
// something special
return new ArrayList<String>();
}
}
ParameterObject could be used to solve the problem when you need to give more objects to the execution. Suppose you have one Integer and a boolean that needs to be given to the execution, this means you will have a class holding an Integer and a boolean like:
public class MyParameter {
private boolean b;
private Integer i;
public MyParameter(Integer i, boolean b) {
this.b = b;
this.i = i;
}
// getters
}
new Executable<Void, MyParameter>() {
public Void execute(MyParameter params) {
// something special
System.out.println(params.getI());
return null;
}
}

Categories