Java:抽象类中的静态字段

Posted

技术标签:

【中文标题】Java:抽象类中的静态字段【英文标题】:Java: static field in abstract class 【发布时间】:2011-06-21 09:19:21 【问题描述】:

我只是从一个例子开始,它解释得最好:

public abstract class A
    static String str;


public class B extends A
    public B()
        str = "123";
    


public class C extends A
    public C()
        str = "abc";
    


public class Main

    public static void main(String[] args)
        A b = new B();
        A c = new C();
        System.out.println("b.str = " + b.str);
        System.out.println("c.str = " + c.str);
    

这将打印出来:

b.str = abc

c.str = abc

但我想要一个解决方案,其中每个实例化超类的子类都有自己的自己的类变量,同时我希望能够通过标识符引用该类变量,或者在抽象超类中定义的方法调用。

所以我希望输出是:

b.str = 123

c.str = abc

这可行吗?

【问题讨论】:

【参考方案1】:

如果您希望类 B 和 C 具有单独的静态变量,则需要在这些类中声明变量。基本上,静态成员和多态性不能一起使用。

请注意,通过引用访问静态成员在可读性方面真的是个坏主意 - 它看起来就像它取决于引用的值,当它没有。因此,当您将 str 移至 B 和 C 时,您当前的代码甚至无法编译。相反,您需要

System.out.println("b.str = " + B.str);
System.out.println("c.str = " + C.str);

如果您确实需要多态访问值(即通过 A 的实例),那么一种选择是制作多态 getter:

public class A 
    public abstract String getStr();


public class B extends A 
    private static String str = "b";

    @Override public String getStr() 
        return str;
    

(C 也一样)。

这样你就可以得到你想要的行为,因为每个实例没有单独的变量,但你仍然可以多态地使用它。实例成员返回这样的静态值有点奇怪,但是您正在使用该值来实现类型的多态性,基本上......

【讨论】:

我的经验是,设置 Eclipse 将它们标记为编译错误甚至是值得的。 @biziclop:我也是。这就是为什么我首先在​​ Eclipse 中请求它:bugs.eclipse.org/bugs/show_bug.cgi?id=21109 :) 为什么这个答案没有被标记为解决问题? 但是如果我想在抽象类的静态方法中引用str,既然getter是一个实例方法,我该怎么做呢? @CameronHudson:你不能,而且你需要确切地指定你想要静态变量来自哪个类。从根本上说,在不了解更多背景信息的情况下很难给出建议。我建议您提出一个具有详细背景的新问题 - 您的要求与 8 年前这个问题的要求完全相同的可能性非常小。【参考方案2】:
public abstract class A 
    private String str;
    public String getStr()  return str;
    protected void setStr(String str)  this.str = str; 

那么你就可以拥有

B b = new B();
b.getStr();

setter 和 getter 是我添加的,你可以简单地将变量设置为非静态。

更新如果你想拥有每个子类的静态,那么你可以拥有:

protected static Map<Class, String> values;
public abstract String getValue();

然后:

public String getValue() 
    values.get(getClass());

public void setValue(String value) 
    values.set(getClass(), value);

但这通常是个坏主意。

【讨论】:

即使没有 getter 和 setter 也可以。然后只需确保 str 变量是公开的,不建议这样做。 @Thijs:比起直接访问静态字段,我总是更喜欢一种方法(尤其是像getStr() 这样的只读方法)。 谢谢!不知道为什么你认为这是一个坏主意。有了这个解决方案,我什至不必在子类中编写任何代码,一切都在超类中实现。当然,在对象上调用方法来设置类变量(或者像你的解决方案那样伪造它,有点)看起来有点奇怪。但似乎无论如何都没有办法逃脱(Jon Skeet 和 Ralph 的解决方案)。 @Bozho:你能解释一下为什么这是个坏主意吗? 这很不自然,违反了最小惊讶原则:)【参考方案3】:

将静态变量放在每个子类中,并在抽象超类中添加(非静态)抽象方法:

abstract String getStr();

然后通过返回这个特殊子类的静态字段,在每个子类中实现getStr()方法。

public class B extends A 
 private static String str;

  @Override
  public String getStr() 
    return B.str;
  

【讨论】:

我编辑了我的帖子,是一个错误..对不起。问题是,我需要每个对象都引用一个值,该值在同一子类的所有对象之间共享,但不一定在不同子类的对象之间。【参考方案4】:

系统中只有一个静态变量实例。

静态变量将在加载类之前的开始加载到系统中。

两次打印 abc 的原因是因为您在最后一次将 str 的值设置为 abc。

【讨论】:

【参考方案5】:

这将打印你想要的输出:

public abstract class A


public class B extends A
    static String str;

    public B()
        str = "123";
    


public class C extends A
    static String str;

    public C()
        str = "abc";
    


public class Main

    public static void main(String[] args)
        A a = new B();
        A c = new C();
        System.out.println("B.str = " + B.str);
        System.out.println("C.str = " + C.str);
    

【讨论】:

谢谢。快到了:) 见拉尔夫的回答。可能是我解释得不好。【参考方案6】:

由于您在子类中硬编码值或str,因此您可以执行以下操作:

public abstract class A
    public abstract String getStr();


public class B extends A
   public String getStr()
      return "123";
   


public class C extends A
   public String getStr()
      return "abc";
   

这可以解决你的问题。

当然,那你应该通过方法调用它,像这样:

public class Main

    public static void main(String[] args)
        A b = new B();
        A c = new C();
        System.out.println("b.str = " + b.getStr());
        System.out.println("c.str = " + c.getStr());
    

【讨论】:

【参考方案7】:

我这样做是为了避免在每个子类中实现相同的方法。它基于Bozho的答案。也许它可以帮助某人。

import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author Uglylab
 */
public class SandBox 

    public static void main(String[] args) 
        A b = new B();
        A c = new C();
        System.out.println("b.str = " + B.getStr(b.getClass()));
        System.out.println("c.str = " + C.getStr(c.getClass()));
    


 abstract class A
    protected static Map<Class, String> values = new HashMap<>();


    public static String getStr(Class<? extends A> aClass) 
        return values.get(aClass);
    

    public static void setStr(Class<? extends A> aClass, String s) 
        values.put(aClass, s);
    



 class B extends A
    public B()
        setStr(this.getClass(),"123");
    


 class C extends A
    public C()
        setStr(this.getClass(),"abc");
    

【讨论】:

【参考方案8】:

我认为解决此问题的一种方法是对BC 类使用单例来模仿静态方法和字段。两者都可以扩展抽象类A,但会有自己的值str..

【讨论】:

以上是关于Java:抽象类中的静态字段的主要内容,如果未能解决你的问题,请参考以下文章

我们可以在抽象类中使用静态方法吗?

java中级,知识点归纳

java接口与抽象方法区别

Java中抽象类和接口中均不能定义静态的抽象方法

java抽象类和普通类的区别

java学习笔记总略