问题是在 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?
}
因为“抽象”的意思是:“不实现任何功能”,而“静态”的意思是:“即使没有对象实例也有功能”。这是一个逻辑矛盾。
语言设计不佳。直接调用静态抽象方法比仅仅为使用该抽象方法而创建实例要有效得多。当使用抽象类作为枚举无法扩展的解决方法时尤其如此,这是另一个糟糕的设计示例。希望他们在下一个版本中解决这些限制。
您不能覆盖静态方法,因此使其抽象化将毫无意义。此外,抽象类中的静态方法属于该类,而不是覆盖类,因此无论如何都不能使用。
方法的abstract
注释表明该方法必须在子类中被覆盖。
在 Java 中,static
成员(方法或字段)不能被子类覆盖(这在其他面向对象的语言中不一定如此,请参阅 SmallTalk。)static
成员可能是hidden ,但这与overridden根本不同。
由于不能在子类中覆盖静态成员,因此无法将abstract
注释应用于它们。
顺便说一句 - 其他语言确实支持静态继承,就像实例继承一样。从语法的角度来看,这些语言通常要求在语句中包含类名。例如,在 Java 中,假设您在 ClassA 中编写代码,这些是等价的语句(如果 methodA() 是静态方法,并且没有具有相同签名的实例方法):
ClassA.methodA();
和
methodA();
在 SmallTalk 中,类名不是可选的,所以语法是(注意 SmallTalk 不使用 . 来分隔“主语”和“动词”,而是使用它作为语句终止符):
ClassA methodA.
因为总是需要类名,所以总是可以通过遍历类层次结构来确定方法的正确“版本”。对于它的价值,我偶尔会错过static
继承,当我第一次开始使用它时,我被 Java 中缺乏静态继承所困扰。此外,SmallTalk 是鸭子类型的(因此不支持按合同编程。)因此,它没有abstract
类成员的修饰符。
我也问了同样的问题,这是为什么
由于抽象类说,它不会给出实现并允许子类给出它
所以 Subclass 必须覆盖 Superclass 的方法,
规则 1 -不能覆盖静态方法
因为静态成员和方法是编译时元素,这就是为什么允许静态方法的重载(编译时多态性)而不是覆盖(运行时多态性)
所以,它们不能是抽象的。
没有抽象静态之类的东西 <--- Java Universe 中不允许
这是一种糟糕的语言设计,实际上没有理由说明为什么它不可能。
事实上,这里有一种模式或方式,说明如何在 **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();
这将允许您静态调用您的方法,但能够更改测试环境的实现。
从理论上讲,您也可以在 a 上执行此操作ThreadLocal
,并且能够为每个 Thread 上下文设置实例,而不是像这里看到的那样完全全局,然后可以执行Request.withRequest(anotherRequestImpl, () -> { ... })
或类似操作。
现实世界通常不需要这种ThreadLocal
方法,通常能够在全球范围内改变测试环境的实现就足够了。
请注意,这样做的唯一目的是使一种方法能够保留调用静态方法提供的直接、简单和干净的方法的能力,同时能够在以稍微更复杂为代价的情况下切换 实现 执行。
这只是一种解决通常不可修改的静态代码的模式。
定义抽象方法只是为了在子类中重写它。但是,静态方法不能被覆盖。因此,具有抽象的静态方法是编译时错误。
现在下一个问题是为什么不能覆盖静态方法?
这是因为静态方法属于一个特定的类而不是它的实例。如果您尝试覆盖静态方法,您将不会收到任何编译或运行时错误,但编译器只会隐藏超类的静态方法。
根据定义,静态方法不需要知道this
. 因此,它不能是虚拟方法(根据通过 获得的动态子类信息重载this
);相反,静态方法重载仅基于编译时可用的信息(这意味着:一旦您引用超类的静态方法,您即调用超类方法,但绝不会调用子类方法)。
据此,抽象静态方法将毫无用处,因为您永远不会将其引用替换为某个已定义的主体。
我看到已经有无数的答案,但我没有看到任何实际的解决方案。当然,这是一个真正的问题,没有充分的理由在 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
}
}
doWork()
C1
和中的方法C2
是相同的。可能有很多这样的类:C3
C4
等等。如果static abstract
允许,您可以通过执行以下操作来消除重复的代码:
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
}
}
但这不会编译,因为static abstract
不允许组合。但是,这可以通过static class
构造来规避,这是允许的:
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();
}
假设有两个类,Parent
和Child
。Parent
是abstract
。声明如下:
abstract class Parent {
abstract void run();
}
class Child extends Parent {
void run() {}
}
这意味着任何实例Parent
必须指定如何run()
执行。
但是,现在假设Parent
不是abstract
。
class Parent {
static void run() {}
}
这意味着Parent.run()
将执行静态方法。
方法的定义abstract
是“已声明但未实现的方法”,这意味着它本身不返回任何内容。
方法的定义static
是“无论在哪个实例上调用它,都为相同参数返回相同值的方法”。
方法的abstract
返回值会随着实例的变化而变化。一个static
方法不会。static abstract
方法几乎是一种返回值是常量但不返回任何内容的方法。这是一个逻辑矛盾。
此外,方法确实没有太多理由static abstract
。
抽象类不能有静态方法,因为抽象是为了实现动态绑定而静态方法静态绑定到它们的功能。静态方法意味着行为不依赖于实例变量,因此不需要实例/对象。只需要类。静态方法属于类而不是对象。它们存储在称为PERMGEN 的内存区域中,从该区域与每个对象共享。抽象类中的方法动态绑定到它们的功能。
将方法声明为static
意味着我们可以通过其类名来调用该方法,如果该类也是abstract
如此,那么调用它是没有意义的,因为它不包含任何主体,因此我们不能将方法声明为static
和abstract
。
由于抽象方法属于该类,不能被实现类覆盖。即使存在具有相同签名的静态方法,它也会隐藏该方法,不会覆盖它。因此,将抽象方法声明为静态是无关紧要的,因为它永远不会获得主体。因此,编译时错误。
可以在没有类实例的情况下调用静态方法。在您的示例中,您可以调用 foo.bar2(),但不能调用 foo.bar(),因为对于 bar,您需要一个实例。以下代码将起作用:
foo var = new ImplementsFoo();
var.bar();
如果您调用静态方法,它将始终执行相同的代码。在上面的例子中,即使你在 ImplementsFoo 中重新定义了 bar2,对 var.bar2() 的调用也会执行 foo.bar2()。
如果 bar2 现在没有实现(这就是抽象的意思),您可以调用没有实现的方法。这是非常有害的。
我相信我已经找到了这个问题的答案,即为什么接口的方法(像父类中的抽象方法一样工作)不能是静态的。这是完整的答案(不是我的)
基本上静态方法可以在编译时绑定,因为要调用它们,您需要指定一个类。这与实例方法不同,您从中调用方法的引用的类在编译时可能是未知的(因此只能在运行时确定调用哪个代码块)。
如果您正在调用静态方法,则您已经知道实现它的类或它的任何直接子类。如果你定义
abstract class Foo {
abstract static void bar();
}
class Foo2 {
@Override
static void bar() {}
}
那么任何Foo.bar();
调用显然都是非法的,你会一直使用Foo2.bar();
.
考虑到这一点,静态抽象方法的唯一目的是强制子类实现这样的方法。您可能最初认为这是非常错误的,但如果您有一个泛型类型参数<E extends MySuperClass>
,最好通过接口来保证E
can .doSomething()
。请记住,由于类型擦除,泛型仅在编译时存在。
那么,有用吗?是的,也许这就是 Java 8 允许在接口中使用静态方法的原因(尽管只有默认实现)。为什么不在类中使用默认实现抽象静态方法?仅仅因为具有默认实现的抽象方法实际上是一个具体方法。
为什么不抽象/接口静态方法没有默认实现?显然,仅仅是因为 Java 识别它必须执行哪个代码块的方式(我的答案的第一部分)。
因为抽象类是一个面向对象的概念,而静态成员不是面向对象的一部分......
现在我们可以在接口中声明静态完整方法,我们可以通过在接口中声明主方法来执行接口
interface Demo
{
public static void main(String [] args) {
System.out.println("I am from interface");
}
}
因为抽象方法总是需要通过子类实现。但是如果你将任何方法设为静态,那么这个方法就不可能覆盖
例子
abstract class foo {
abstract static void bar2();
}
class Bar extends foo {
//in this if you override foo class static method then it will give error
}
拥有一个抽象静态方法的想法是你不能直接为那个方法使用那个特定的抽象类,但是只有一阶导数才能实现那个静态方法(或者对于泛型:你的泛型的实际类采用)。
这样,您可以创建一个 sortableObject 抽象类,甚至可以创建与(自动)抽象静态方法的接口,这些方法定义了排序选项的参数:
public interface SortableObject {
public [abstract] static String [] getSortableTypes();
public String getSortableValueByType(String type);
}
现在您可以定义一个可排序对象,该对象可以按所有这些对象相同的主要类型进行排序:
public class MyDataObject implements SortableObject {
final static String [] SORT_TYPES = {
"Name","Date of Birth"
}
static long newDataIndex = 0L ;
String fullName ;
String sortableDate ;
long dataIndex = -1L ;
public MyDataObject(String name, int year, int month, int day) {
if(name == null || name.length() == 0) throw new IllegalArgumentException("Null/empty name not allowed.");
if(!validateDate(year,month,day)) throw new IllegalArgumentException("Date parameters do not compose a legal date.");
this.fullName = name ;
this.sortableDate = MyUtils.createSortableDate(year,month,day);
this.dataIndex = MyDataObject.newDataIndex++ ;
}
public String toString() {
return ""+this.dataIndex+". "this.fullName+" ("+this.sortableDate+")";
}
// override SortableObject
public static String [] getSortableTypes() { return SORT_TYPES ; }
public String getSortableValueByType(String type) {
int index = MyUtils.getStringArrayIndex(SORT_TYPES, type);
switch(index) {
case 0: return this.name ;
case 1: return this.sortableDate ;
}
return toString(); // in the order they were created when compared
}
}
现在您可以创建一个
public class SortableList<T extends SortableObject>
that can retrieve the types, build a pop-up menu to select a type to sort on and resort the list by getting the data from that type, as well as hainv an add function that, when a sort type has been selected, can auto -sort new items in。注意SortableList的实例可以直接访问“T”的静态方法:
String [] MenuItems = T.getSortableTypes();
必须使用实例的问题是 SortableList 可能还没有项目,但已经需要提供首选排序。
切里奥,奥拉夫。
首先,关于抽象类的一个关键点——抽象类不能被实例化(参见wiki)。因此,您不能创建抽象类的任何实例。
现在,java 处理静态方法的方式是与该类的所有实例共享该方法。
所以,如果你不能实例化一个类,那么这个类就不能有抽象的静态方法,因为抽象方法需要被扩展。
繁荣。
根据 Java文档:
静态方法是与定义它的类相关联的方法,而不是与任何对象相关联的方法。类的每个实例共享其静态方法
在 Java 8 中,除了默认方法之外,接口中还允许使用静态方法。这使我们更容易在我们的库中组织辅助方法。我们可以将特定于接口的静态方法保留在同一个接口中,而不是在单独的类中。
一个很好的例子是:
list.sort(ordering);
代替
Collections.sort(list, ordering);
doc本身也给出了另一个使用静态方法的例子:
public interface TimeClient {
// ...
static public ZoneId getZoneId (String zoneString) {
try {
return ZoneId.of(zoneString);
} catch (DateTimeException e) {
System.err.println("Invalid time zone: " + zoneString +
"; using default time zone instead.");
return ZoneId.systemDefault();
}
}
default public ZonedDateTime getZonedDateTime(String zoneString) {
return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
}
}
因为“抽象”意味着该方法将被覆盖,并且不能覆盖“静态”方法。
当常规方法打算被子类覆盖并提供功能时,它们可以是抽象的。想象一下这个类Foo
是由 etc 扩展的Bar1, Bar2, Bar3
。所以,每个人都会根据自己的需要拥有自己的抽象类版本。
现在,根据定义,静态方法属于类,它们与类的对象或其子类的对象无关。它们甚至不需要它们存在,它们可以在不实例化类的情况下使用。因此,它们需要随时可用,并且不能依赖子类向它们添加功能。
因为 abstract 是应用于抽象方法的关键字,所以不指定主体。如果我们谈论静态关键字,它属于类区域。
因为如果您在类中使用任何静态成员或静态变量,它将在类加载时加载。
有一种情况是 static 和 abstract 可以一起使用,即这两个修饰符都放在嵌套类的前面。
静态方法 无需创建类的实例即可调用静态方法。静态方法属于类而不属于类的对象。静态方法可以访问静态数据成员,也可以更改它的值。 Abstract Keyword 用于实现抽象。不能在子类中覆盖或实现静态方法。因此,将静态方法设为抽象是没有用的。
因为如果一个类扩展了一个抽象类,那么它必须重写抽象方法,这是强制性的。并且由于静态方法是在编译时解析的类方法,而重写方法是在运行时解析并遵循动态多态性的实例方法。
您可以使用 Java 8 中的接口来执行此操作。
这是关于它的官方文档:
https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html