I am writing some classes and all of them implement a certain method they inherit from an interface. This method is close to the same for all the classes beside one call to a certain other function.
For example:
public void doSomething(){
int a = 6;
int b = 7;
int c = anOtherMethod(a,b);
while(c < 50){
c++;
}
}
What if multiple classes have the function doSomething() but the implementation of the method anOtherMethod() is different?
How do I avoid code duplication in this situation? (This is not my actual code but a simplified version that helps me describe what I mean a bit better.)
This looks like a good example of the template method pattern.
Put doSomething in a base class.
Declare abstract protected anotherMethod in that base class as well, but don't provide an implementation.
Each subclass then provides the proper implementation for anotherMethod.
This is how you could implement the technique that Thilo talked about in the following demo:
Main class:
public class Main extends Method {
public static void main(String[] args) {
Method m = new Main();
m.doSomething();
}
#Override
public int anOtherMethod(int a, int b) {
return a + b;
}
}
Abstact class:
public abstract class Method {
public abstract int anOtherMethod(int a, int b);
public void doSomething() {
int a = 6;
int b = 7;
int c = anOtherMethod(a, b);
System.out.println("Output: "+c);
}
}
This way, all you have to do is override anOtherMethod() in each class that you want to use doSomething() with a different implementation of the method anOtherMethod().
Assuming every version of anOtherFunction takes two integers and returns an integer, I would just have the method accept a function as an argument, making it Higher Order.
A function that takes two arguments of the same type and returns an object of the same type is known as a BinaryOperator. You can add a argument of that type to the method to pass a function in:
// Give the method an operator argument
public void doSomething(BinaryOperator<Integer> otherMethod) {
int a = 6;
int b = 7;
// Then use it here basically like before
// "apply" is needed to call the passed function
int c = otherMethod.apply(a,b);
while(c < 50)
c++;
}
}
How you use it though will depend on your use case. As a simple example using a lambda, you can now call it like:
doSomething((a, b) -> a + b);
Which simply returns the sum of a and b.
For your particular case though, you may find that having doSomething as part of a Interface isn't necessary or optimal. What if instead, anOtherMethod is what's required to be supplied? Instead of expecting your classes to supply a doSomething, have them supply a BinaryOperator<Integer>. Then, when you need to get results from doSomething, get the operator from the class, then pass it to doSomething. Something like:
public callDoSomething(HasOperator obj) {
// There may be a better way than having a "HasOperator" interface
// This is just an example though
BinaryOperator<Integer> f = obj.getOperator();
doSomething(f);
}
Related
I'm learning about interfaces and I faced a problem which I cannot understand. It's about lambda expression which I learn that can be used in one line, but I've create a case, where this expression does not working and I don't know where I've made a mistake.
I have defined this interface:
public interface MathOperations {
int add(int a, int b);
}
Next I have defined class in which I want to test this interface with adder method.
public class App {
public static void main(String[] args) {
int x;
x = adder((a, b) -> return a+b);
}
public static int adder(MathOperations mo){
return mo.add(3, 5);
}
}
Unfortunately the line with the x assignment doesn't work and I cannot figure out where I've made a mistake. The compiler does not recognize the a and b variable in the return statement. I know that I can make this assignment with brackets but I'm curious if I can do this in one line.
#FunctionalInterface
interface MathOperations {
int add(int a, int b);
}
Here are two ways to do what you want.
To create a lambda, you need to specify the interface type and provide the definition of what to do with a and b.
then you can invoke the lambda and get the value.
MathOperations compute = (a,b)->a+b;
x = compute.add(2,3);
System.out.println(x);
The above does essentially the following behind the scenes.
Here an anonymous class is defined using the interface and instantiated using the new keyword. (the class contains the implemented method).
Implementing the interface was standard until the concept of FuntionalInterfaces and lambdas were introduced in Java 8.
Then call the adder method supplying the instance mo just created or call the method with the previously created compute lambda.
MathOperations mo =new MathOperations() {
public int add(int a,int b) {
return a+b;
}};
System.out.println(adder(mo));
System.out.println(adder(compute));
output of above three print statements
5
8
8
public static int adder(MathOperations mo){
return mo.add(3, 5);
}
Note: Imo, a more versatile interface can be created by following examples from the API functional interfaces already defined. The name MathOperation is fine but the add method is too specific (and functional interfaces may only contain one abstract method). So instead of having an add method, have a compute or similar method and let the name of the lambda dictate its operation.
MathOperation add = (a,b)->a+b;
int sum = add.compute(10,20); // 30
MathOperation sub = (a,b)->a-b;
int diff = sub.compute(10,20) // -10
I have a problem with my abstract class, I have an app for about 2000 users and many make calls at the same time to different classes, those classes share an abstract class
class A extends B{
//Code
}
The inconvenience arises when in my abstract class I receive parameters to some of my methods that in this case all classes call these methods.
class abstract B {
public int getResult(int b){
return b+1;
}
}
class C extends B {
public int getValue(int a){
int b = getResult(a);
int c = b * 2 / 4;
return c + 150;
}
}
When several people in parallel enter the class 'C' and pass different parameters to the method getResult ends up mixing the data sent and returns erroneous data in the execution.
The option that I have found is to implement the getResult method in each class, but that makes me repeat code in each class and there are about 200 classes that implement that abstract class
I've read that with encapsulation I can avoid that, but I don't know if that's the option since I haven't worked on it.
I have used the synchronized but it has created many bottlenecks and response times have increased.
I think you need use synchronized in your getValue method to manage parallel access to yout method. Maybe this article can help you, have a simple example too: https://dzone.com/articles/java-concurrency-synchronization#:~:text=the%20credit%20method
Note B class must have _"abstract" keyword before class.
Add "synchronized" in your getValue method
This way:
abstract class B {
public int getResult(int b){
return b+1;
}
}
class C extends B {
public synchronized int getValue(int a){
int b = getResult(a);
int c = b * 2 / 4;
return c + 150;
}
}
From my understanding all function-calls in Java are virtual, and numeral literals have the type int. But why does the Output in the example below differ?
public class A {
public int f(long d) {
return 2;
}
}
public class B extends A {
public int f(int d) {
return 1;
}
}
public class M {
public static void main(String[] args) {
B b = new B();
A ab = b;
System.out.println(b.f(1));
System.out.println(ab.f(1));
}
}
You dont override anything.
The first calling System.out.println(b.f(1)); returns 1, because it works with class B, even the method is named same, but parameters are different (long is not the same as int).
In case when parameters are same (int d), the result would be 1, because it overrides (#Override) the method from the class A.
Now, you know why the second calling System.out.println(ab.f(1)); returns 2. Look from what class it's called from.
Actually the subclass B has inherited the method f(with a long) and has added (overloaded) another method f(with a int).
When you write down a value such as 1 in such a way the compiler, even before assigning it to a typed reference does parse it as an int. More here: Java's L number (long) specification .
When you call the method f using the reference ab (which is A class) it (the reference) says I can only send you to a method f(with a long) and then implicitly cast the type int 1 into a long.
Let's try to change the type of the method in A class to f(short),
then System.out.println(ab.f(1)); will give you this error:
"The method f(short) in the type A is not applicable for the arguments (int)"
Sometime I need to make callback from one method to another:
final Integer i = 5;
final Integer j = 10;
foo("arg1", "arg2", (x, y) -> {
/** do sth which need this context **/
bar(i + x, j - y);
})
But in such case I need to write simple interface (somewhere):
public interface someName {
void noMatterName(Integer a, Integer c);
}
Then function foo() can call noMatterName - this is ok. But in simple cases, name of such interfaces and its function is not important. I just want to use lambda with two parameters.
Question:
Do I need to create this interface manually even if I need to make such "communication" between only two function? Does Java provide any similar interface?
Or even something like this:
public interface someName1 {
void noMatterName(Object a);
}
public interface someName2 {
void noMatterName(Object a, Object b);
}
You can use the Consumer<T> (if you need a single parameter), and BiConsumer<T,U> (if you need 2 parameters) functional interfaces.
Interface Consumer<T>
Represents an operation that accepts a single input argument and returns no result.
Interface BiConsumer<T,U>
Represents an operation that accepts two input arguments and returns no result.
I have a small problem in my code
I have 2 classes
public class A {
public A foo(int a) {return new A();}
}
public class B extends A{
public B foo(int x){ return new B();}
}
now in my code I want to print only the method that was declared in class B
in this way
B b = new B();
Method[] m = b.getClass().getDeclaredMethods();
for (int i = 0; i < m.length; i++) {
System.out.print(m[i].getName());
}
why the output is
foo
foo
why the GetDeclaredMethods finds also the foo in the A class?
how can i fix it?
thanks
The reason you are having a problem is because of the covariant return types of your two methods. Because you have a covariant return type (the return type of B is B, not A, unlike the superclass), Java under the hood generates a separate method with the original return type to act as a bridge between the pre-1.5 bytecode specification the new Java 1.5 language behavior.
The method you should be using to check, though is the isBridge() method, as it expresses exactly what you intend to exclude. So the final code would look something like this:
Method[] methods = B.class.getDeclaredMethods();
for (Method method : methods) {
if (!method.isBridge()) {
System.out.println(method.getName());
}
}
By default, getDeclaredMethods() returns all of the methods for the given class, as well as it's parent classes and interfaces. However, the Method object allows you to test which class a Method belongs to by calling getDeclaringClass() on that Method. So when you cycle through all the Method objects, you can add logic to only print a method if it belongs to the B class.
Method[] m = b.getClass().getDeclaredMethods();
for (int i = 0; i < m.length; i++) {
if (m[i].getDeclaringClass().equals(B.class)) {
System.out.print(m[i].getName());
}
}
EDIT: The above code doesn't work as desired -- it returns B as the declaring class of all methods. The isSynthetic() method appears to work as desired, returning true for an overridden method (one that came from A), but false for one that came from B. So the following code might be what you're looking for.
Method[] m = b.getClass().getDeclaredMethods();
for (int i = 0; i < m.length; i++) {
if (!m[i].isSynthetic()) {
System.out.print(m[i]);
}
}
Because B.foo and A.foo is different methods. If you want to override method A.foo, then method B.foo must return class A.
You can call m.getDeclaringClass() to see if it's the Method from Class A or Class B.
This may work:
A a = new A();
B b = new B();
List<Method> aMethods = Arrays.asList(a.getClass().getMethods());
List<Method> bMethods = Arrays.asList(b.getClass().getMethods());
for ( Method m : bMethods )
{
if( ! aMethods.contains(m) )
{
//Your action code here
}
}
When you says if( ! aMethods.contains(m) ) does contains compare by name? arguments type? return value type? because the only difference from the wanted method to the not is the covariance return type...