Why Can't Static Methods Be Abstract in Java

Why can't static methods be abstract in Java?

The abstract annotation to a method indicates that the method MUST be overriden in a subclass.

In Java, a static member (method or field) cannot be overridden by subclasses (this is not necessarily true in other object oriented languages, see SmallTalk.) A static member may be hidden, but that is fundamentally different than overridden.

Since static members cannot be overriden in a subclass, the abstract annotation cannot be applied to them.

As an aside - other languages do support static inheritance, just like instance inheritance. From a syntax perspective, those languages usually require the class name to be included in the statement. For example, in Java, assuming you are writing code in ClassA, these are equivalent statements (if methodA() is a static method, and there is no instance method with the same signature):

ClassA.methodA();

and

methodA();

In SmallTalk, the class name is not optional, so the syntax is (note that SmallTalk does not use the . to separate the "subject" and the "verb", but instead uses it as the statemend terminator):

ClassA methodA.

Because the class name is always required, the correct "version" of the method can always be determined by traversing the class hierarchy. For what it's worth, I do occasionally miss static inheritance, and was bitten by the lack of static inheritance in Java when I first started with it. Additionally, SmallTalk is duck-typed (and thus doesn't support program-by-contract.) Thus, it has no abstract modifier for class members.

Is exposing static methods in abstract classes considered good or bad practice

It's not bad practice per se, unless the abstract class is actually not meant to be subclassed, and abstract is only there to prevent instantiation. The best practice for a class providing only static utilities would be to make it final, and to have a private constructor.

Java: static abstract (again) - best practice how to work around

To restate the problem: you want your per-file-type classes to have statically available information on the type (e.g., name and description).

We can easily get part-way there: create a separate class for your type info, and have a static instance of this (appropriately instantiated) in each per-file-type class.

package myFileAPI;

public class TypeInfo {
public final String name;
public final String description;

public TypeInfo(String name, String description) {
this.name = name;
this.description = description;
}
}

and, say:

package myFileAPI;

public class TextFile {
public static final TypeInfo typeInfo
= new TypeInfo("Text", "Contains text.");
}

Then you can do stuff like:

System.out.println(TextFile.typeInfo.name);

(Of course, you could also use getters in TypeInfo to encapsulate the underlying strings.)

However, as you said, what we really want is to enforce the existence of a particular signature static method in all your per-file-type classes at compile time, but the 'obvious' design path leads to requiring an abstract static method in a common superclass which isn't allowed.

We can enforce this at run-time though, which may be good enough to ensure it is coded correctly. We introduce a File superclass:

package myFileAPI;

public abstract class File {

public static TypeInfo getTypeInfo() {
throw new IllegalStateException(
"Type info hasn't been set up in the subclass");
}

}

If TextFile now extends File, we will get this exception when calling TextFile.getTypeInfo() at runtime, unless TextFile has a same-signature method.

This is quite subtle: code with TextFile.getTypeInfo() in still compiles, even when there is no such method in TextFile. Even though static methods are bound at compile time, the compiler can still look through the class hierarchy to determine the compile-time static call target.

So, we need code like:

package myFileAPI;

public class TextFile extends File {

private static final TypeInfo typeInfo
= new TypeInfo("Text", "Contains text.");

// Shadow the superclass static method
public static TypeInfo getTypeInfo() {
return typeInfo;
}

}

Note that we are still shadowing the superclass method, and so File.getTypeInfo() can still be 'meaninglessly' called.

Static abstract method workaround

You can create a Factory to produce the animal objects, Below is a sample to give you a start:

public void myMainFunction() {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(AnimalFactory.createAnimal(Bird.class,birdXML));
animals.add(AnimalFactory.createAnimal(Dog.class,dogXML));
}

public abstract class Animal {
/**
* Every animal subclass must be able to be created from XML when required
* (E.g. if there is a tag <bird></bird>, bird would call its 'createFromXML' method
*/
public abstract Animal createFromXML(String XML);
}

public class Bird extends Animal {
@Override
public Bird createFromXML(String XML) {
// Implementation of how a bird is created with XML
}
}

public class Dog extends Animal {
@Override
public Dog createFromXML(String XML) {
// Implementation of how a dog is created with XML
}
}

public class AnimalFactory{
public static <T extends Animal> Animal createAnimal(Class<T> animalClass, String xml) {
// Here check class and create instance appropriately and call createFromXml
// and return the cat or dog
}
}

How can I implement abstract static methods in Java?

Essentially what you are asking for is the ability to enforce, at compile time, that a class defines a given static method with a specific signature.

You cannot really do this in Java, but the question is: Do you really need to?

So let's say you take your current option of implementing a static getIdentity() in each of your subclasses. Consider that you won't actually need this method until you use it and, of course, if you attempt to use it but it isn't defined, you will get a compiler error reminding you to define it.

If you define it but the signature is not "correct", and you attempt to use it differently than you have defined it, you will also already get a compiler error (about calling it with invalid parameters, or a return type issue, etc.).

Since you can't call subclassed static methods through a base type, you're always going to have to call them explicitly, e.g. GInteger.getIdentity(). And since the compiler will already complain if you try and call GInteger.getIdentity() when getIdentity() isn't defined, or if you use it incorrectly, you essentially gain compile-time checking. The only thing you're missing, of course, is the ability to enforce that the static method is defined even if you never use it in your code.

So what you have already is pretty close.

Your example is a good example that explains what you want, but I would challenge you to come up with an example where having a compile-time warning about a missing static function is a necessity; the only thing I can think of that sort of comes close is if you are creating a library for use by others and you want to ensure that you don't forget to implement a particular static function -- but proper unit testing of all your subclasses can catch that during compile-time as well (you couldn't test a getIdentity() if it wasn't present).

Note: Looking at your new question comment: If you are asking for the ability to call a static method given a Class<?>, you cannot, per se (without reflection) -- but you can still get the functionality you want, as described in Giovanni Botta's answer; you will sacrifice compile-time checks for runtime-checks but gain the ability to write generic algorithms using identity. So, it really depends on your end goal.



Related Topics



Leave a reply



Submit