hidding api with interfaces in Java - java

Can somebody explain me what "hidding api with interfaces in Java" means ?
How can I use the API functions by means of interfaces ?
I would need an small working example to understand the how the interfaces hides the api non public parts, and how can I use the api puplic parts in the same time.
Thanks in advance.
THANKS GUYS FOR THE QUICK REPLY, GIVE ME TIME TO THINK OVER THE ANSWERS.
LAST BUT NOT LEAST THANKS FOR YOUR TIME AND EFFORT!
II. My second question is : What happens in the background this case below ?
IBinaryOperation op = BinaryOperationProvider.getOperation("multiply");
or
List<String> myList = new LinkedList<String>();
Its not clear for me because the interfaces consist of methods' declarations that's why i dont understand what could happened in the lines above.
Is there any meaning of the equality between empty method of used interfaces and objects ?
Sorry for my weak English.

For instance, you may declare and create a list of strings as follows:
List<String> myList = new LinkedList<String>();
List is the type of myList. It is an interface. It means that all subsequent calls to methods of myList will be done through the interface: you may only call methods declared in the List interface.
However, the concrete class of the object is LinkedList, that contains more methods, some of them reflecting the structure of a linked list (for instance addFirst and addLast). But these methods are hidden because of the way you declared the variable. You chose to access the object through a given (restrictive) interface.
It may seem restrictive, but it also means that you can change your code at any time, and replace the concrete class LinkedList with any other class that implements the List interface, for example ArrayList.

Usually when you expose your API, you should hide the implementation details as much as possible and expose it via simple interfaces.
For e.g. Suppose that you give an api for adding two numbers.
Soln1 (Bad soln) Give the following class to client
public class Adder {
public void setA() {..}
public void setB() {..}
public int add() { return A + B; }
}
Soln 2 (better soln): Give the following interface to the client.
public interface Adder {
public int add(int a, int b);
}
Now why is soln 2 a better solution. If you had given user the first soln. The client is bound to the class Adder. Suppose later you have a new implementation of addition that could add the numbers in the cloud(over-imaginative :)), you may have to as the client to change their code to use the new class.
Instead if you just give them the interface, you could provide many implementation and have a factory mechanism to choose the suitable implementation.

Here's a very simple example that uses an interface:
public interface IBinaryOperation {
public int performOp(int a, int b);
}
private class MultiplicationProvider implements IBinaryOperation {
public int performOp(int a, int b) {
return a * b;
}
}
public class BinaryOperationProvider {
static IBinaryOperation getOperation(String name) {
if ("multiply".equals(name)) {
return new MultiplicationProvider();
} else if ("add".equals("name)) {
return new AdditionProvider();
} // ...
}
}
You would use this like:
IBinaryOperation op = BinaryOperationProvider.getOperation("multiply");
int c = op.performOp(a, b);
In the above example, MultiplicationProvider is completely private to the implementation of the API. The only public part is the IBinaryOperation interface, and the BinaryOperationProvider class.

Just to be clear, what's "hidden" is not the API, but the implementation. Most clients of List (to use an example above) don't need to know which kind of List is actually being used. (Just like most TCP/IP clients don't need to know anything in particular about the network they're using -- just that it supports the standard connection abstraction.)

Related

How to use generics with interfaces

I'm fairly new to programming and we do have an exercise, we have to use the Consumer interface, we have a generic class (genClass) that has a Consumer<T> attribute (conAtt). In another class we have to use the accept method of Consumer, but somehow it doesn't work. I have already read through the Java API of the Consumer interface, but it didn't help.
The error message says:
The method accept(capture#4-of ?) in the type Consumer<capture#4-of ?> is not applicable for the arguments (capture#5-of ?)
I know it says not applicable, but why not?
public abstract class GenClass<T> {
protected Consumer<T> conAtt;
public abstract T getData();
}
class Otherclass{
private List<GenClass<?>> helparray= new ArrayList<>();
private void testmethod() {
Iterator<GenClass<?>> hilfe = helparray.iterator();
while (hilfe.hasNext()) {
GenClass<?> help = hilfe.next();
help.conAtt.accept(help.getData());//here is the problem
}
}
}
public class thirdclass extends GenClass<Character> {
#Override
public Character getData() {
return 't';//t is just an example
}
}
This is not really a question about how the Consumer - or other interfaces - in Java work, but about Generics.
Generics aim to simplify the way of writing code and avoid code repetitions. E.g. you need to do a similar task, but for different types you can write it once by using Generics instead of writing it over and over again, just with concrete types being replaced.
For example one day you have the need to keep track of a list of Strings. As easy as that, your going ahead and implementing a solution for that, whereby the first implementation can look like the following (note: a very simplified example, but it'll show the purpose):
public class CustomListString {
private String[] elements = new String[10];
public void add(String newElement) {
int nextFreeIndex = findNextFreeIndex();
elements[nextFreeIndex] = newElement;
}
public String get(int index) {
return elements[index];
}
}
So you can use the above implementation of the List in your code like the following:
public static void main(String[] args) {
CustomListString listOfStrings = new CustomListString();
listOfStrings.add("A");
listOfStrings.add("B");
}
Simple, specific and sufficient!
But the other day, you also have the requirement to keep track of a list of Integers. What to do now?
A way to solve this is to just repeat your previous approach and to implement another CustomList only for the Integers now. Where the corresponding implementation would look like this (the implementation of CustomListString has been copied and all occurrences of String have been replaced by Integer):
public class CustomListInteger {
private Integer[] elements = new Integer[10];
public void add(Integer newElement) {
int nextFreeIndex = findNextFreeIndex();
elements[nextFreeIndex] = newElement;
}
public Integer get(int index) {
return elements[index];
}
}
As you can imagine now already, this is not flexible and can be very cumbersome in the future. This approach will require a new implementation of each type you want to store in the future. So you might end up to also create implementations like CustomListDouble, CustomListCharacter, ... and so on, in which only the type of the elements within the array change - nothing else which would be of importance!
This will additionally lead to the situation, that you'll duplicate a lot of similar code (like findNextFreeIndex() method would have been) and in case of a bugfix need to adjust it in a lot of places instead of in only one.
To solve this issue and remain the type safety in the CustomList.get method Generics have been introduced to Java!
With the Generics approach you'll be able to create a single implementation of the CustomList to store all of your data types without unnecessarily duplicating any shared, basic code and remain the type safety!
public class CustomList<T> {
private Object[] elements = new Object[10]; // Java doesn't supprort easily support generic arrays, so using Object
// here. But the compiler ensures only elements of the generic type T
// will end up here
public void add(T newElement) {
int nextFreeIndex = findNextFreeIndex();
elements[nextFreeIndex] = newElement;
}
#SuppressWarnings("unchecked")
public T get(int index) {
return (T) elements[index];
}
}
Using the new list following the Generics approach we can use it like this now:
public static void main(String[] args) {
CustomList<String> genericList = new CustomList<>();
genericList.add("Hello World");
genericList.add(5); // Compile error! Integer and String types cannot be mixed in
// a single instance of the list anymore => Nice, prevents errors!
genericList.get(0).substring(6); // No compile error, also the compiler knows Strings
// are contained in the list
}
The generic CustomList can now also be reused for any other type and still provide type safety.
What does it mean for your implementation
You can see how we specified the generic type in the CustomList class as T - this is similar like you specified it with ? (probably you'll also want to replace it with T, since you'll run into other issues later when working with the Consumer). But when we used the implementation in our other classes, it wouldn't have been possible to specify it as CustomList<T> or CustomList<?> anymore. We needed to decide and specifiy which exact type of elements the list should contain. This has been the String class, so we specified it as CustomList<String>.
Note: ? is a generic wildcard and means something like "I don't know the real type of the classes now and I'll also don't know it in the future". That's why it'll be hard for you working with the concrete types later in the Consumer. You'll be not able to call any conrete methods on your objects therein. Therefore ? should be avoided as a generic type argument and something like T should be used instead. T means something like "I don't know the real type of the classes now, but I'll do later, as soon as you tell me". Therfore you'll be able to call concrete methods on the objects later in the Consumer, what will simplify your work there a lot.
For your code this means, wherever you want to use your implementation of GenClass<T> you need to specify with which exact kind of elements the class is going to work with. In case of String it is GenClass<String> in case of Character GenClass<Character>.
So the place you'll need to replace the occurrences of GenClass<?> is wherever you refer to it in Otherclass and Otherclass.testmethod.
The way you used the Consumer is fine

Using a comparable on 3 different classes

I'm trying to implement a function that returns the maximum object of a given Comparable (generic) list.
I have 3 classes that I have implemented their compareTo method that returns the 1 if this is bigger than other, -1 if this is smaller than other, and 0 if they're equal.
Now my problem is with understanding with how do I work with a generic input COmparable list.
Here's the signature of my function, and the code I wrote so far (that refuses to work on me):
public static Comparable<?> getMax(List<Comparable<?>> ls) {
LinkedList<Comparable<?>> tmpComp = new LinkedList<Comparable<?>>();
for (Comparable<?> c : ls)
tmpComp.add(c);
Comparable<?> maxObj = tmpComp.get(0);
for (Comparable<?> c : tmpComp)
if (c.compareTo(maxObj) > 0)
m = c;
return m;
}
I'm writing a system that has users in it, and ads. Users and ads both classes that have "profit" field on them that all I do in my compareTo methods is to compare which of the two (this, or other) have more profit and then just returns the right value according to that. The 3rd class is compared via another field, which is an int as well, that indicates the level (int) of the Quest.
Also that if statement, specifically, gives me an error of the type "is not applicable for the arguments".
Any clues?
Thanks in advance!
Reading your comment, I suggest you redesign your model to be:
interface ProfitGenerating {
double getProfit();
}
class User implements ProfitGenerating {
...
}
class Advert implements ProfitGenerating {
...
}
List<ProfitGenerating> profits = ...;
Optional<ProfitGenerating> maxProfit = profits.stream()
.max(Comparator.comparingDouble(ProfitGenerating::getProfit));
The answer by Mạnh Quyết Nguyễn is good. But it does not account for the situation where you have multiple potential types T, which appears to be your situation.
So in that situation, just wrap your various classes with a single class and use his solution.
If you have a User class and an Ad class, then create a wrapper like so:
class ProfitMaker implements Comparable<ProfitMaker> {
User user;
Ad ad;
public int compare(ProfitMaker p) {
//check my profit and compare with profit of p
}
}
Use that class as the "T" when usign the getMax from Mạnh Quyết Nguyễn.
Alternatively, use an interface
interface ProfitMaker extends Comparable<ProfitMaker> {
int getProfit();
}
Make both your User and Ad classes implement that interface, and that use that interface as the "T" along with the getMax method from Mạnh Quyết Nguyễn.
Your three classes must be comparable to each other. For this they will need to implement Comparable<SomeX> where SomeX is their lowest common superclass. In the worst case, SomeX is Object.
If this is the case, you can simply do:
ls.stream().max(Comparator.naturalOrder())
Alternatively, instead of forcing your classes to implement Comparable<...>, you could capture comparison semantics in a Comparator<...> and then do:
ls.stream().max(comparator)
Using a comparator is better for cases where the order is not really "natural" for the type or where there may be different orders. I think this is the case here since you actually compare instances of different types. It is hard to argue that some order is "natural" for these instances as they don't even belong to one type.
If you compare your instances based on some property they share (like int getProfit()), it would make sense creating a common interface like Profitable. Then you could do:
ls.stream().max(Comparator.comparintInt(Profitable::getProfit))
Note that if you compare on privitive types, you should use comparingInt/comparingLong/comparingDouble instead of comparing to avoid unnecessary boxing and unboxing.
If you for some reason can't create and implement a common interface like Profitable, you can still use comparingInt and likes. You'll just have a much uglier lambda:
ls.stream().max(Comparator.comparintInt(l -> {
if (l instanceof Ad) { return ((Ad) l).getProfit(); }
else if (l instanceof Ransom) { return ((Ransom) l).getProfit(); }
// ...
else { throw new IllegalArgumentException(...); }
}))

Java: Create a Generic Method out of multiple methods

I came across a piece of code where two methods have very similar functionalities, return the same type, but are different.
private Set<String> extractDeviceInfo(List<Device> devices){
Set<String> sets= new HashSet<>();
for(Device item:items){
sets.add(item.getDeviceName());
}
return sets;
}
private Set<String> extractDeviceInfoFromCustomer(List<Customer> customers){
Set<String> sets= new HashSet<>();
for (Customer c : customers) {
sets.add(c.getDeviceName());
}
return sets;
}
As you can see from the code above, both methods are returning the same Set and retrieving the same data.
I'm trying to attempt to create a generic method out of it and did some research but couldn't find anything that could solve this issue.
If I understand this correctly, using generics, I can define generic parameters in the method and then pass parameters as well as the class type when calling the method. However I am not sure what to do after wards.
For example, the method getDeviceName() how can I call it out of a generic class as the compiler doesn't know whether the generic class has that method or not.
I will really appreciate if someone could tell me whether this is possible and how to achieve the desired result.
Thanks
UPDATE: Creating an interface and then having implementation looks like a good solution but I feel like it's overdoing when it comes to refactoring a couple of methods to avoid boiler plate.
I've noticed that Generic classes can be passed as a parameter and the have methods like getMethod() etc.
I was wondering if it was possible to create a generic method where you pass the class as well as the method name and then the method resolves that at runtime
eg.
private <T> Set<String> genericMethod(Class<T> clazz, String methodName ){
clazz.resolveMethod(methodName);
}
So basically, I could do this when calling the method:
genericMethod(Customer.class,"gedDeviceInfo");
I believe there's one language where this was achievable but not sure if you can do it in Java, although, a few years back I remember reading about resolving string into java code so they get compiled at runtime.
Both Device and Customer should implement the same interface where the method getDeviceName is defined:
interface Marker {
String getDeviceName();
}
class Device implements Marker { ... }
class Customer implements Marker { ... }
I named it Marker, but it's up to you to name it reasonably. Then, the method might look like:
private Set<String> extractDeviceInfo(List<? extends Marker> markers) {
return markers.stream().map(Marker::getDeviceName).collect(Collectors.toSet());
}
It allows the next type variations:
extractDeviceInfo(new ArrayList<Device>());
extractDeviceInfo(new ArrayList<Customer>());
extractDeviceInfo(new ArrayList<Marker>());
99% of the time Andrew answer is the solution. But, another approach is to define the function in parameter.
This can be useful for some reporting or if you need to be able to extract values from an instance in multiple ways using the same method.
public static <T, U> Set<U> extractInfo(List<T> data, Function<T, U> function){
return data.stream().map(function).collect(Collectors.toSet());
}
Example :
public class Dummy{
private String a;
private long b;
public Dummy(String a, long b){ this.a = a; this.b = b; }
public String getA(){return a; }
public long getB(){return b; }
}
List<Dummy> list = new ArrayList<>();
list.add(new Dummy("A1", 1));
list.add(new Dummy("A2", 2));
list.add(new Dummy("A3", 3));
Set<String> setA = extractInfo(list, Dummy::getA); // A1, A2, A3
Set<Long> setB = extractInfo(list, Dummy::getB); // 1, 2, 3
using reflection in java will take a performance hit. in your case, it's probably not worth it.
There is nothing wrong with your original code, if there are less than 3 places using it, DO NOT refactor. If there is more than 3 places and expecting more coming, you can refactor using #andrew's method.
you should not refactor code just for the sake of refactoring in my opinion.

How to get rid of the inheritance?

I have an algorithm, and I have 2 different implementations of the algorithm. These implementations should be called from many places, depending on the mode selected by the user. I wouldn't like to write conditional statements at all places where implementations called. So, I create an abstract class and Implementations inherit it. I can set the desired mode in one place like this:
if(firstMode){
list = new ListForm1();
}
else{
list = new LiastForm2();
}
And after that in all other places I can enjoy all the benefits of polymorphism.
It works good but I want to get rid of the inheritance of the following reasons:
I heard that composition is much better than inheritance.
The first form of the algorith is much easier then the second form. In the first form I have only 3 methods and in second form I have 15 methods. The abstract class had to include all 15 (and 5 common methods). It turns out that the 12 methods not using by the first form.
Theoretically, there may be a new form of the algorithm, which will have even less in common with the other two, but it will bring 10 new methods and all of them will have to add an abstract class.
The Strategy Pattern, as I understand, does not make sense to use here.
Here is the example of Strategy Pattern:
//abstract strategy
interface Strategy {
int execute(int a, int b);
}
// concrete strategy1
class ConcreteStrategyAdd implements Strategy {
public int execute(int a, int b) {
return a + b;
}
}
// concrete strategy2
class ConcreteStrategySubtract implements Strategy {
public int execute(int a, int b) {
return a - b;
}
}
//concrete strategy3
class ConcreteStrategyMultiply implements Strategy {
public int execute(int a, int b) {
return a * b;
}
}
class Context {
private Strategy strategy;
public Context() {
}
// Set new concrete strategy
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
// use strategy
public int executeStrategy(int a, int b) {
return strategy.execute(a, b);
}
}
It has the same problems. Strategies should be linked with each other. If I link them with the interface instead of an abstract class it will be even worse. Interface will contain a lot of methods but many of them will not be needed for the first form of the algorithm. In addition, general methods have to duplicate in all concrete strategies. I can not provide a default implementation in the interface.
Moreever, I don't understand how to use composition here. As I understand, Strategy Pattern already used composition. Class Context includes the instance of Strategy as a field. But maybe it is delegation.
So, here is my question:
Can I get rid of all the above problems (too many methods of an abstract class, the strong connection, because of which it will be difficult to add a new form of an algorithm), but still use conditional statements in only one place, not in all cases when I need some form of algorithm.
UPD:
I want to show how I called some methods, which implemented in SECOND form of the algorithm, but not need for the FIRST form of algorithm:
if (list.getCurrentLeaders().contains(ballIdx))
The default implementation of method getCurrentLeaders() return null. So, if I called it with instance of the FIRST form of the algorithm then I will get an error. I understand that it is bad. But how can I solve it?
Starting from the beginning in the case you need to call a different algorithm based on a different mode chosen by the user you could create a kind of factory class to supply the algorithm throughout your code. I think that if it is only an algorithm and if you are on Java 8 you can use a Function or a Predicate or a Supplier in combination with a map to avoid the if statement, for example :
Map<String, Predicate<Whatever>> map = new HashMap<>();
map.put("mode_one", (w) -> true);
map.put("mode_two", (w) -> false);
Then to call the algorithm, simply :
map.get("mode_one").test()
In the case you need to supply a different form like in the example you posted, you could use a Supplier instead of a predicate.
Based on your simple requirement, I think that going functional would be the best bet ...
If you are not implementing all the methods (ie. if you have 15 methods in the abstract class to be implemented, and you only need to implement 10), then you are breaking the Liskov Substitution Principle :
https://en.wikipedia.org/wiki/Liskov_substitution_principle
Basically, that is a bad thing.
Try and convert the non-common methods into some other kind of object that gets passed into the constructor (on the abstract).
You can implement some kind of Chain Of Responsibility pattern.
interface IStrategy {
void Run();
bool CanHandle(IContext context);
}
class StrategyChecker {
IStrategy GetStrategy(IContext context) {
foreach(var strategy in strategies) {
if(strategy.CanHandle(context)
return strategy;
}
return defaultStrategy;
}
}
class Director {
void Run() {
strategyChecker.AddStrategy(strategy1);
strategyChecker.AddStrategy(strategy2);
var strategy = strategyChecker.GetStrategy(someContext);
strategy.Run();
}
}
Sorry for c# pseudo-code.
I heard that composition is much better than inheritance.
Not always - many times inheritance is the right construct. You have to think about it in has a and is a terms. A football team has a collection pf players. It also has a coach, a schedule, a name, etc. So Team : List<Player> is not the right construct.
A Car is a Vehicle, so inheritance is the right construct.
So think about your design this way:
Do my classes share a common base? Is there a base class that makes sense to say ListForm1 is a ListBase and ListForm2 is a ListBase. What methods and properties are common to those types that should be in the case type? What methods and properties should be virtual so that I can override them?
The first form of the algorithm is much easier then the second form. In the first form I have only 3 methods and in second form I have 15 methods. The abstract class had to include all 15 (and 5 common methods). It turns out that the 12 methods not using by the first form.
So maybe your base type only 3 methods, and you add methods in the sub-types as necessary. Remember that you can have multiple base types in the chain, but it's a chain, not a tree, meaning you can have a single parent that has another parent, but you can't have two parents.
Or maybe you have orthogonal interfaces since you can implement multiple interfaces.
Theoretically, there may be a new form of the algorithm, which will have even less in common with the other two, but it will bring 10 new methods and all of them will have to add an abstract class.
Why? Why can't the new algorithm just define its own methods that it needs, so long as clients pick the appropriate level in the inheritance chain (or appropriate interface(s)) so that it knows what methods should be implemented.
if (list.getCurrentLeaders().contains(ballIdx))
The default implementation of method getCurrentLeaders() return null. So, if I called it with instance of the FIRST form of the algorithm then I will get an error. I understand that it is bad. But how can I solve it?
So do you need to check that this particular list implements an interface (or inherits a base class) that does implement that method?
Why not just use your IStrategy as a type?
interface IStrategy {
int execute(int a, int b);
}
class Strategy1 implements IStrategy {}
class Strategy2 implements IStrategy {}
static class StrategyFactory {
IStrategy Create(bool first) {
return first ? new Strategy1() : new Strategy2();
}
}
And then in your user code:
void doStuff()
{
IStrategy myStrategy = StrategyFactory.Create(true);
myStrategy.execute(1, 2);
}

Can Java store methods in arrays?

Well I wrote some code and all I was doing was for loops, but changing which method I called. I tried using a for loop so it'd be a bit neater (and out of curiosity to see if it could be done), but it doesn't compile when I do it this way, because it doesn't recognize an item in an array as a method, I think. This is what I have:
String[] moveArray = {moveRight,moveDown,moveLeft,moveUp};
for (i = 0; i < 4; i++) {
while (myWumpus.moveArray[i]) {
myWumpus.moveArray[i];
generator.updateDisplay();
}
}
When I try compile I get
not a statement myWumpus.moveArray[i]();
';' expected myWumpus.moveArray[i]();
(It refers to the first statement in the while loop)
So, I think it's maybe because I'm making it an Array of type String? Is there a type Method? Is this at all possible? Any solutions welcome :). Also, I can get it to work using 4 while loops, so you don't need to show me that solution.
You cannot store methods directly in arrays. However you can store objects, which implement the same method differently. For example:
Mover[] moveArray = {new RightMover(), new DownMover() new LeftMover(), new UpMover() };
for (i = 0; i < 4; i++) {
while (myWumpus.moveArray[i]) {
moveArray[i].move();
generator.updateDisplay();
}
}
Yes, you can store methods in arrays using Reflection, however it is likely that what you actually want to do in this situation is use polymorphism.
As an example of polymorphism in relation to your problem - say you created an interface as follows:
public interface MoveCommand {
void move();
}
You can then create implementations as follows:
public class MoveLeftCommand implements MoveCommand {
public void move() {
System.out.println("LEFT");
}
}
etc. for the other move options. You could then store these in an MoveCommand[] or collection like a List<MoveCommand>, and then iterate over the array/collection calling move() on each element, for example:
public class Main {
public static void main(String[] args) {
List<MoveCommand> commands = new ArrayList<MoveCommand>();
commands.add(new MoveLeftCommand());
commands.add(new MoveRightCommand());
commands.add(new MoveLeftCommand());
for (MoveCommand command:commands) {
command.move();
}
}
}
Polymorphism is very powerful, and the above is a very simple example of something called the Command Pattern. Enjoy the rest of your Wumpus World implementation :)
You can't store methods in arrays in Java, because methods aren't first-class objects in Java. It's a reason some people prefer to use other languages like Python, Scheme, etc.
The work-around is to create an interface which contains one method, then create four classes implementing that interface - the MoveRight, MoveLeft, etc... classes. Then you can store instances of those classes in your array and call them all the same way.
You can't call methods like that. But you can using reflection:
Just change the first line in the while-loop to:
Method m = myWumps.getClass().getMethod(moveArray[i]); // if the method is void
m.invoke(myWumps);
(you will have to declare/catch a few exceptions)
But you'd better avoid reflection, and use the Command pattern instead.
Updated answer for Java 8 and onwards-
Since the introduction of lambda expressions and method references in Java 8, storing various methods in variables is now possible. One main issue is that arrays don't currently support generic objects in Java, which makes storing the methods in arrays less doable. However they can be stored in other data structures like a List.
So for some simple examples you can write something like:
List<Comparator<String>> stringComparators = new ArrayList<>();
Comparator<String> comp1 = (s1, s2) -> Integer.compare(s1.length(), s2.length());
stringComparators.add(comp1);
or
List<Consumer<String>> consumers = new ArrayList<>();
Consumer<String> consumer1 = System.out::println;
consumers.add(consumer1);
and then loop/iterate through the List to get the methods.

Categories