DMeneses
DMeneses

Reputation: 161

Correct ways to enforce implementation of inherited static methods in Java?

My goal is to create an abstract class whose static methods have to be implemented, but I was having some issues due to static methods not being able to be made abstract.

This is the solution I came up with:

public abstract class CombinedMethod {
    public static String computeMethodBody() throws
            CannotCompileException {
        throw new NotImplementedException();
    }
    public static ArrayList<CtMethod>  sortSelectedMethods() {
        throw new NotImplementedException();
    }
}

I'm making this post because I couldn't find any equivalent answer, which left me wondering if this is idiomatic in Java.

Edit to add use case:

I want to create several classes that must all implement both computeMethodBody and sortSelectedMethods. My solution adds structure and semantic meaning to the code, compared to, for example, creating documentation explaining how to create equivalent classes.

I am aware of why overriding static methods doesn't make sense in Java and that I'm just hiding them. As I said there's no other answer exemplifying this use case, but there's plenty discussing the concept.

Edit to add more details about the project:

The goal here is to implement some features of Common Lisp's method combination, using annotations as modifiers.

To name an example, suppose Class2 inherits from Class1:

public class Class1 {
    ...
    @Combination("+")
    public int myValue()
    {
        System.out.println("In myValue of Class1");
        return 1;
    }
}

public class Class2 extends Class1 {
    ...
    @Combination("+")
    public int myValue()
    {
        System.out.println("In myValue of Class2");
        return 2;
    }
}

Since they're both annotated with "+", I want to change the behaviour of this method at load time, so that Class2's effective method will be something behaviourally equivalent to:

    @Combination("+")
    public int myValue()
    {
        System.out.println("In myValue of Class2");
        System.out.println("In myValue of Class1");
        return 2 + 1;
    }

To do so, I employed Javassist to compute a new effective method. Fortunately, I was able to generalize my architecture, so adding new functionality entails creating a static method and calling it inside a new switch case statement:

    String computeEffectiveMethodBody(String annotationValue, CtClass ctClass, ArrayList<CtMethod> sortedSelectedMethods) throws CannotCompileException {
        switch (annotationValue) {
            case "min":
                return CombinedMin.computeMethodBody(ctClass, sortedSelectedMethods);
            case "max":
                return CombinedMax.computeMethodBody(ctClass, sortedSelectedMethods);
            case "+":
                return CombinedAddition.computeMethodBody(ctClass, sortedSelectedMethods);
            case "and":
                return CombinedAnd.computeMethodBody(ctClass, sortedSelectedMethods);
            case "or":
                return CombinedOr.computeMethodBody(ctClass, sortedSelectedMethods);
            default:
                throw new RuntimeException("Invalid annotation");
        }
    }

The way I choose to segregate it was by creating a package called combinedMethods. Inside it, there's the parent function CombinedMethod and another subpackage called methods where all of the extensions with the actual static methods are kept.

Thank you @AasmundEldhuset for your interest, I'm always looking for the best way to architecture my software, even though I haven't learned it formally yet.

Upvotes: 0

Views: 989

Answers (2)

Alexander Ivanchenko
Alexander Ivanchenko

Reputation: 29038

enforce implementation of inherited static methods in Java?

Java Language Specification says:

A class does not inherit private or static methods from its superinterface types.

Static methods are also called class methods. They are bound to a class and don't require an instance of the class in order to be invoked.

static and abstract is an illegal combination of modifiers because static methods are completely self-contained and always have an implementation.

You can not inherit static methods and as subsequence the concept of overriding is not applicable to them. And since a subclass can not override a parent's static method, abstract static method doesn't make sense because it can not be implemented.

A class method can be hidden by a method from a subclass with the same signature.

A quote from JLS:

A class (static) method that is hidden can be invoked by using a reference whose type is the type of the class that actually contains the declaration of the method. In this respect, hiding of static methods is different from overriding of instance methods.

I.e. hidden version can be invoked only on a child class or instance, conversely to the overridden method which can be invoked on the instance on of parent class or on the instance on of child class.

In other words, you can't obtain polymorphic behavior with static methods.


UPDATE

I want to create several classes that must all implement both computeMethodBody and sortSelectedMethods

So you want these two static methods with the same signature to be present in a couple of classes. And that's totally fine. But you don't need a parent abstract class for that because there's no useful code in it if both its method will be hidden.

creating documentation explaining how to create equivalent classes

Inheritance in Object-Oriented Programming isn't used for documentary purposes. The child should be capable to replace its parent in any use-cases, as Liskov substitution principle suggests.

The problem is that there are no use-cases for such a parent class. combinedMethod class isn't designed for inheritance (BTW, it's not a very informative name and by convention names of classes and interfaces should start with a capital letter).

You are misusing inheritance. If you need to provide the end-users of your classes with some additional information, there are other ways to do that:

  • The very first mean is a self-documenting code. Use clear, concise and self-explanatory names for your methods and classes.
  • Since Java 5 metadata can be provided with annotations.
  • In earlier versions, marker-interfaces were used for that purpose. But classes were never utilized for that.

Another thing that you need to understand is that although inheritance is a very important and useful mechanism, it also has pitfalls and should be applied after careful consideration.

Even if your case would be more suitable to apply inheritance, it wouldn't automatically mean that inheritance is the best option.

For instance, classes IntSummaryStatistics, LongSummaryStatistics and DoubleSummaryStatistics have no parent class in the JDK, although they have common fields and behavior.

Take a look at these classes a compare with your situation when parent isn't designed to be extended and has nothing to offer to its subclasses.

If you need to provide metadata - you can create a custom annotation like @CapableOfSomething and mark your classes with it, but don't abuse inheritance instead.

Upvotes: 1

GeertPt
GeertPt

Reputation: 17874

Static methods are invoked on a class, independent of an instance, so if they would be abstract, how would the run-time know on which sub-class to call them?

CombinedMethod cm1 = new SubclassA(...)
CombinedMethod cm2 = new SubclassB(...)

// static method computeMethodBody is called on neither cm1 or cm2, so what implementation to choose.
String result = CombinedMethod.computeMethodBody();

Upvotes: 0

Related Questions