问题是,在Java中为什么不能定义抽象静态方法?例如
abstract class foo {
abstract void bar( ); // <-- this is ok
abstract static void bar2(); //<-- this isn't why?
}
问题是,在Java中为什么不能定义抽象静态方法?例如
abstract class foo {
abstract void bar( ); // <-- this is ok
abstract static void bar2(); //<-- this isn't why?
}
当前回答
这是一个糟糕的语言设计,真的没有理由不可能。
事实上,这里有一个模式或方法可以在**Java中模仿它,让你至少能够修改自己的实现:
public static abstract class Request {
// Static method
public static void doSomething() {
get().doSomethingImpl();
}
// Abstract method
abstract void doSomethingImpl();
/////////////////////////////////////////////
private static Request SINGLETON;
private static Request get() {
if ( SINGLETON == null ) {
// If set(request) is never called prior,
// it will use a default implementation.
return SINGLETON = new RequestImplementationDefault();
}
return SINGLETON;
}
public static Request set(Request instance){
return SINGLETON = instance;
}
/////////////////////////////////////////////
}
两种实现:
/////////////////////////////////////////////////////
public static final class RequestImplementationDefault extends Request {
@Override void doSomethingImpl() {
System.out.println("I am doing something AAA");
}
}
/////////////////////////////////////////////////////
public static final class RequestImplementaionTest extends Request {
@Override void doSomethingImpl() {
System.out.println("I am doing something BBB");
}
}
/////////////////////////////////////////////////////
可以这样使用:
Request.set(new RequestImplementationDefault());
// Or
Request.set(new RequestImplementationTest());
// Later in the application you might use
Request.doSomething();
这将允许您静态地调用方法,同时还能够更改例如Test环境的实现。
理论上,您也可以在ThreadLocal上执行此操作,并且能够为每个线程上下文设置实例,而不是像这里所示的完全全局,然后可以执行Request。withRequest(anotherRequestImpl,() ->{…})或类似的。
现实世界通常不需要ThreadLocal方法,通常能够全局地改变测试环境的实现就足够了。
请注意,这样做的唯一目的是能够保留静态方法所提供的直接、轻松和干净地调用方法的能力,同时能够切换实现,以牺牲稍微复杂的实现为代价。
它只是一种绕过通常不可修改的静态代码的模式。
其他回答
首先,关于抽象类的一个关键点—— 抽象类不能被实例化(参见wiki)。因此,您不能创建抽象类的任何实例。
现在,java处理静态方法的方法是与该类的所有实例共享该方法。
所以,如果你不能实例化一个类,这个类就不能有抽象静态方法,因为抽象方法需要扩展。
繁荣。
方法的抽象注释表明该方法必须在子类中重写。
在Java中,静态成员(方法或字段)不能被子类覆盖(在其他面向对象语言中不一定是这样,请参阅SmallTalk)。静态成员可能被隐藏,但这与被覆盖有本质区别。
由于静态成员不能在子类中被重写,因此抽象注释不能应用于它们。
顺便说一句,其他语言确实支持静态继承,就像实例继承一样。从语法的角度来看,这些语言通常要求在语句中包含类名。例如,在Java中,假设你在ClassA中编写代码,这是等价的语句(如果methodA()是一个静态方法,并且没有具有相同签名的实例方法):
ClassA.methodA();
and
methodA();
在SmallTalk中,类名不是可选的,所以语法是(注意,SmallTalk不使用。将“主语”和“动词”分开,而是将其用作语句结束符):
ClassA methodA.
因为总是需要类名,所以总是可以通过遍历类层次结构来确定方法的正确“版本”。无论如何,我偶尔会错过静态继承,当我第一次开始使用Java时,我就被Java中缺乏静态继承所困扰。此外,SmallTalk是duck类型的(因此不支持契约式编程)。因此,类成员没有抽象修饰符。
假设有两个类,父类和子类。父母是抽象的。声明如下:
abstract class Parent {
abstract void run();
}
class Child extends Parent {
void run() {}
}
这意味着Parent的任何实例都必须指定如何执行run()。
但是,现在假设Parent不是抽象的。
class Parent {
static void run() {}
}
这意味着Parent.run()将执行静态方法。
抽象方法的定义是“声明但未实现的方法”,这意味着它本身不返回任何东西。
静态方法的定义是“对于相同的参数返回相同值的方法,而不管调用它的实例是什么”。
抽象方法的返回值会随着实例的改变而改变。静态方法则不会。静态抽象方法基本上是这样一种方法,它的返回值是常量,但不返回任何东西。这是一个逻辑矛盾。
同样,使用静态抽象方法的理由也不多。
因为如果一个类扩展了一个抽象类,那么它必须重写抽象方法,这是强制性的。由于静态方法是在编译时解析的类方法,而覆盖方法是在运行时解析的实例方法,并遵循动态多态性。
我看到已经有无数的答案,但我没有看到任何实际的解决方案。当然,这是一个真正的问题,没有很好的理由在Java中排除这种语法。由于最初的问题缺乏上下文,因此我提供了上下文和解决方案:
假设你在一堆相同的类中有一个静态方法。这些方法调用特定于类的静态方法:
class C1 {
static void doWork() {
...
for (int k: list)
doMoreWork(k);
...
}
private static void doMoreWork(int k) {
// code specific to class C1
}
}
class C2 {
static void doWork() {
...
for (int k: list)
doMoreWork(k);
...
}
private static void doMoreWork(int k) {
// code specific to class C2
}
}
C1和C2中的doWork()方法相同。可能有很多这样的类:C3 C4等。如果允许静态抽象,你可以通过这样做来消除重复代码:
abstract class C {
static void doWork() {
...
for (int k: list)
doMoreWork(k);
...
}
static abstract void doMoreWork(int k);
}
class C1 extends C {
private static void doMoreWork(int k) {
// code for class C1
}
}
class C2 extends C {
private static void doMoreWork(int k) {
// code for class C2
}
}
但这将无法编译,因为静态抽象组合是不允许的。 然而,这可以通过静态类构造来避免,这是允许的:
abstract class C {
void doWork() {
...
for (int k: list)
doMoreWork(k);
...
}
abstract void doMoreWork(int k);
}
class C1 {
private static final C c = new C(){
@Override void doMoreWork(int k) {
System.out.println("code for C1");
}
};
public static void doWork() {
c.doWork();
}
}
class C2 {
private static final C c = new C() {
@Override void doMoreWork(int k) {
System.out.println("code for C2");
}
};
public static void doWork() {
c.doWork();
}
}
使用此解决方案,唯一重复的代码是
public static void doWork() {
c.doWork();
}