Advertising:
Call from one java class to another class
One of the basic questions for java beginners is how to call methods or access attributes of a java class from another class. Let's look at several options here.
Any questions about this or about Java in general I usually answer in the java forum
Class A instantiates class B and calls a method of class B
If from class A we want to call methods of class B, the most immediate thing is for class A to make the new of class B and call its method. For example, if class B is
public class B {
public void aMethodOfB() {
System.out.println("I am B");
}
}
Class A would just have to do something like this
public class A {
public void aMethodOfA() {
B b = new B();
b.aMethodOfB();
}
}
Of course, any variant is valid, make the new of B in the constructor and save it to use it later or whatever we want
public class A {
private B b = new B();
public void aMethodOfA() {
b.aMethodOfB();
}
}
Pass class B to class A from outside. Inversion of control
The above method may not always be valid. Sometimes in the main or in another place we make the new of classes A and B and we are not interested in A making another new of B. For these cases, the solution is to pass it to A, either in the constructor or in a method made for that purpose, class B so A can use it. The code for A could be either of the following
public class A {
private B b;
public A(B b){
this.b = b;
}
public void aMethodOfA() {
b.aMethodOfB();
}
}
public class A {
private B b;
public void setB(B b) {
this.b = b;
}
public void aMethodOfA() {
b.aMethodOfB();
}
}
And in the place where we do the new, we would do one of the following things
B b = new B();
A a = new A(b);
a.aMethodOfA();
B b = new B();
A a = new A();
a.setB(b);
a.aMethodOfA();
This is known as "inversion of control" or "IoC". Class A, instead of doing the new, waits for someone to do it and pass the instance to the Class A.
Use of interfaces
With the above procedure, passing class B to class A, interfaces are often used, although not necessary. The idea is that B implements an InterfaceB
and that A expects to receive that interface, such as
public interface InterfaceB {
public void aMethodOfB();
}
public class B implements InterfaceB {
@Override
public void aMethodOfB() {
System.out.println("I am B");
}
}
public class A {
private InterfaceB b;
public A (InterfaceB b) {
this.b = b;
}
public void aMethodOfA() {
b.aMethodOfB();
}
}
The advantages of using interface instead of directly passing class B are mainly two. Imagine class A is the algorithm for a wonderful game you just made and class B is the graphical user interface. When A calls b.aMethodOfB()
it is actually telling the user interface to draw a move that our game algorithm has just decided on. Imagine now that you have another project in which the game is the same, but this time the game is on the web and user interface B is not useful, you have to make another web interface and also, your algorithm A is going to run on the server, but the web interface runs in client browsers.
The first advantage of using interfaces is the following: If we don't use an interface and A directly uses B, in our new project we would have to take both class A and class B, even though the last is not useful. Either our server will have to load the whole useless GUI , or else we will have to modify the code of class A so that it doesn't make use of this class B. If we use an interface, then our server could have only class A and the InterfaceB
, much lighter than the full class B. Also, our class A could be a bit clever and do something like
public class A {
private InterfaceB b = null;
public void setB (InterfaceB b) {
this.b = b;
}
public void aMethodOfA() {
if ( null != b) {
b.aMethodOfB();
}
}
}
that is, A is smart enough to call b only if it has been previously passed to it. So we don't have to touch anything in A to take it to our new web game.
The second advantage is the following: In our web game, possibly when A decides to make a move he will have to call someone to paint it, but in this case it may be to send that move through a socket or a connection instead of calling directly to the graphical user interface B. Having made the interface, in our new game we can make a Connection
class in charge of sending the moves decided by A to the web page that the client is viewing. We just have to make our new Connection
class implement InterfaceB
and we can use it exactly like we used class B before, without needing to touch A at all.
public class Connection implements InterfaceB {
@Override
public void aMethodOfB() {
System.out.println("I am not B but Connection, so I do different things than B");
}
}
Connection c = new Connection();
A a = new A(c);
a.aMethodOfA();
In summary, the two advantages of using interfaces are:
- Our class is more reusable, we can take it to other projects without having to take many more classes.
- Our class can change its behavior by calling future classes that we make simply by implementing the interface.
Complicating the matter
The example we've done for A and B is pretty simple in the sense that there's a piece of code that does new A and B and then passes B to A. But in a more complex program, this can get messy. Imagine that we have a class AA that is the one that makes the new of A and another class BB that is the one that makes the new of B and we still need A to be able to call methods of B. The procedure that we must follow is the same, but we need to put setB()
and getB()
methods everywhere, so that wherever the new of AA and BB are done, we can get B and pass it to A. The code can be like this
For the one hand, AA must have a setB()
that passes B to A.
public class AA {
private A a = new A();
public void setB (InterfaceB b) {
a.setB(b);
}
}
On the other hand, BB must have a getB()
method that allows us to get B
public class BB {
private InterfaceB b = new B();
public InterfaceB getB() {
return b;
}
}
And our main()
would do something like this
AA aa = new AA();
BB bb = new BB();
aa.setB(bb.getB());
Service Locator
Things can get really complicated, there can be many more classes that we need to pass back and forth, not just B. And there can be many levels of classes, like AAAA making new from AAA which in turn makes new from AA and in turn of A, so calls to getB()
and setB()
can grow a lot.
When a class B is going to be used in many places and we want to avoid all the "mess" of putting getters and setters in the classes, there is another alternative that is to use what is known as a service locator . Actually, there are many variants, such as service locator, singleton, factories, etc, etc, but the basic idea of all these mechanisms is the same and we are going to explain how.
The idea is to have a class, let's call it Service
, that is the one that has the instance of B and a method to get it. Since we want to be able to call this class anywhere, both the instance of B and the method must be static. Something like that
public class Service {
private static InterfaceB b = new B();
public static InterfaceB getB() {
return b;
}
}
Done, being static
, we can call it from anywhere in our code by doing this
public class A {
public void aMethodOfA() {
Service.getB().aMethodOfB();
}
}
This mechanism is very useful when class B is something that will be used in many places in our program, such as a database access class, sockets, etc.
The downside as we have done it is that if we want to reuse A in another code, we have to take the Service
class and to take this one, we also have to take class B, even though we are using the Interface , Why? because the Service class is directly making a new of B and therefore, without B, it doesn't work.
To avoid this, we need to prevent Service
from doing new directly, so we put a set method on it, like so
public class Service {
private static InterfaceB b;
public static setB(InterfaceB b) {
this.b = b;
}
public static InterfaceB getB() {
return b;
}
}
And in our main()
we will have to do this
Service.setB (new B());
A a = new A();
a.aMethodOfA();
We have already achieved what we wanted, we can take A, Service and InterfaceB to any other project without having to take class B, which is no longer useful to us. Also, in the new web game project we discussed earlier, we could pass Service
the class Connection
instead of class B.
Service.setB (new Connection());
A a = new A();
a.aMethodOfA();
We mentioned before that there were several variants of this option: service locator, factories, singleton, etc. The basic difference between all of them is basically where the new of class B is made and whether the same instance of B is always returned or different instances are returned each time Service.getB()
is called. .
The version of Service
we just saw is a service locator. Services (class B) are "registered" at some point (Service.setB(new B());)
and can be used anywhere from there Service.getB( ).aMethodOfB();
Inversion of Control or Service Locator?
Which is better? Of course, it is a matter of taste. The usual thing is that when a class is going to be used in many places and you want it to always be the same instance, for example, database connections, communication sockets, etc., a service is used. On the other hand, if class B were a somewhat special class that's only going to be used in a place or two, or we want there to be lots of instances of B going around, like a UI panel with a sales table, a text box that asks for numerical values, etc., then inversion of control would be used, that is, passing to the class A of our code through a setter on which classes B in particular would have to act.
But as always, it is better to use your head than to follow recipes to the letter. The use of one or the other mechanism depends on each specific case and on the possible changes and reuse possibilities that we foresee in our code.
If we don't expect class A to ever be reused, we can skip using InterfaceB
and make it use directly B, making the code simpler and not complicating it for something we'll never use. .
A service locator adds additional complexity, it is one more Service
class that needs to be initialized in our main()
with something like Service.setB(new B) );
and we never know where it will be used (class A, another class C, another class D, ...), so it is quite common to take those classes A,C,D to other projects. .. and forget to register the services they need. So we need to assess whether that extra complexity makes up for the setters and getters we remove by not using inversion of control.