Java中的抽象变量?

65

我来自C#,在那里这很容易实现。

我有这段代码:

public abstract class clsAbstractTable {

    public abstract String TAG;
    public abstract void init();

}

但是Eclipse告诉我使用了非法修饰符。

我有这个类:

public class clsContactGroups extends clsAbstractTable {


}
我希望定义变量和方法,这样Eclipse可以提示我有未实现的抽象变量和方法。 如何定义我的抽象类,以便我能够收到提示来实现这些抽象内容? EDIT 1 我将为不同的数据库表创建不同的类。每个类都应该有自己的TABLENAME变量,没有例外。我必须确保每当我创建一个继承抽象类的新类时,此变量是静态的。
然后在抽象类中,我将有一个名为 init() 的方法;
如果在这个init()方法中调用TABLENAME,它应该从子类中获取值。
类似于这样的东西也应该可以解决问题。
String tablename=(clsAbstract)objItem.TABLENAME;
// where objItem can be any class that extended clsAbstract;

编辑2

我想在每个类中定义一个常量(静态),其名称在抽象中定义。

  • 我在抽象中定义了变量TABLENAME,但没有给出值。
  • 我创建了一个名为clsContactGroups的类,它应提示我实现TABLENAME,这是获取某些数据的地方。例如:TABLENAME="contactgroups";
  • 我创建了第二个类clsContacts,我应该被提示实现TABLENAME,这是获取某些数据的地方。例如:TABLENAME="contacts";
    等等...

4
Java 中不存在抽象变量,但是有抽象方法。 - duffymo
我如何在所有类中被提示定义一个TAG变量? - Pentium10
1
我看不出为什么不能使用像getTableName()这样声明为抽象的方法来完成这个任务。 - Padmarag
这将是每个类的静态内容,我想避免使用getter和setter。它将在每个类中定义一个常量。 - Pentium10
2
抱歉,在C#中也不可能。只有实例方法(以及setter/getter)可以是抽象的。 - Markus Johnsson
显示剩余4条评论
13个回答

72

在抽象类中定义一个构造函数,设置字段,使得具体实现需要调用/覆盖该构造函数来符合规范。

例如:

public abstract class AbstractTable {
    protected String name;
    
    public AbstractTable(String name) {
        this.name = name;
    }
}

如果你继承了AbstractTable,那么这个类在编译之前需要添加一个调用super("somename")的构造函数。

public class ConcreteTable extends AbstractTable {
    private static final String NAME = "concreteTable";

    public ConcreteTable() {
        super(NAME);
    }
}

通过这种方式,实现者需要设置name。这样你还可以在抽象类的构造函数中进行(空)检查,以使其更加健壮。例如:

public AbstractTable(String name) {
    Objects.requireNonNull(name, "Name may not be null");
    this.name = name;
}

请参考我的问题编辑。我希望在每个类中定义一个常量(tablename),并在抽象类中定义它的名称。 - Pentium10
@Kevin:每个实例都要这样做吗?我认为我们用不同的视角看待问题。 - BalusC
@Kevin:哦,那样啊。嗯,那有点微观优化了。我的方法至少需要更少的重复代码和更少的考虑因素。 - BalusC
这里使用IllegalArgument异常是否更好? - TigerBear
1
我喜欢这个设计,易于理解且合乎逻辑。 - pedram bashiri
显示剩余10条评论

55

我认为你对C#属性和字段/变量的区别感到困惑。在C#中,你不能定义抽象字段,即使是在抽象类中也不行。但你可以定义抽象属性,因为这些实际上是方法(例如,编译为get_TAG()set_TAG(...))。

正如一些回答提醒的那样,即使是在C#中,你也不应该在类中有公共字段/变量。有几个答案暗示了我的建议,但并不清楚。你应该将你的想法转换为JavaBean属性,使用getTAG()。然后你的子类将必须实现它(我也写过一个项目,其中的表类就是这样实现的)。

所以你可以定义一个抽象类,像这样...

public abstract class AbstractTable {

    public abstract String getTag();
    public abstract void init();

    ...
}

然后,在任何具体的子类中,您需要定义一个静态的 final 变量(常量),并从 getTag() 中返回它,就像这样:

public class SalesTable extends AbstractTable {

    private static final String TABLE_NAME = "Sales";

    public String getTag() {
        return TABLE_NAME;
    }

    public void init() {
        ...
        String tableName = getTag();
        ...
    }

}

编辑:

你不能在C#或Java中覆盖继承的字段。也不能覆盖静态成员,无论它们是字段还是方法。因此,这也是最佳解决方案。我修改了上面的init方法示例,以展示如何使用它 - 再次将getXXX方法视为属性。


1
+1 我认为这真正回答了楼主的问题,因为问题最初的前提是错误的(即 C# 可以有抽象字段)。 - JonoW
非常喜欢你的解释,非常清晰、准确且简明扼要! - undefined

11

Java(或C ++)中不存在抽象变量。

如果父类有一个变量,并且子类扩展了父类,则子类不需要实现该变量。它只需要访问父类的实例。可以使用get/set或protected访问。

"......所以我应该被提示去实现抽象类吗?" 如果您扩展了抽象类并未实现其中的抽象方法,则编译器将提示您要么实现它,要么将子类标记为抽象类。这是您将得到的所有提示。


我想确保子类定义了那个变量。我该如何确定? - Pentium10
@Pentium10:相较于一开始在基类中定义变量,这样做有什么优势吗? - Joachim Sauer
我将为不同的数据库表创建不同的类。每个类都应该有自己的TABLENAME变量,没有例外。我必须确保每次创建一个扩展抽象类的新类时,这些变量是静态的。 - Pentium10
我认为你不需要每个表一个类。一个类并创建多个实例在内存中将足够。 - duffymo

3

你最好做的是为变量创建访问器/修改器。
例如 getTAG()
这样所有实现类都必须实现它们。

抽象类用于定义抽象行为而不是数据。


是的,我想定义抽象行为,我的意思是定义名称,然后在实现层处理数据。 - Pentium10

2
只需将此方法添加到基类即可。
public abstract class clsAbstractTable {

    public abstract String getTAG();
    public abstract void init();

}

现在,每个继承基类的类(且不想成为抽象类)都应该提供一个TAG。

你也可以选择BalusC的答案。


我编辑了我的问题,希望现在清楚了我想要实现什么。 - Pentium10
1
我认为这是解决方案。至少是Java提供的唯一符合所有要求的解决方案。每个子类都必须提供一个getTAG实现,可以返回该子类的字符串常量。在我看来,这甚至比在原始代码中将其保留在每个实例变量中更好。 - PSpeed
@Joachim:那似乎有点苛刻。我认为Lombo很好地理解了OP,而getter方法确实是更好的实现方式,只是这里没有完全实现。 - Kevin Brock
@duffymo:为什么要打击这个呢?getter确实是OP想要的,因为它最像C#属性(他似乎真正考虑的是这个)。这还要求子类实现getter——就像OP所请求的那样。 - Kevin Brock
@Kevin:看起来OP想要一个“抽象静态”变量(不管那是什么),而给出的建议也没有解决这个问题。 - Joachim Sauer
显示剩余6条评论

1

将代码更改为:

public abstract class clsAbstractTable {
  protected String TAG;
  public abstract void init();
}

public class clsContactGroups extends clsAbstractTable {
  public String doSomething() {
    return TAG + "<something else>";
  }
}

这样,所有继承此类的类都将拥有此变量。您可以创建200个子类,每个子类仍将具有此变量。

顺便说一句:不要使用大写字母作为变量名;常识是所有大写标识符都指代常量,即不可更改的数据片段。


当我创建一个新类时,是否会提示我定义该类的标签(TAG)? - Pentium10
正如一些人提醒的那样,你永远不应该在你的类中使用公共字段/变量。但是,在公共最终类中使用public final字段没有任何问题,这只是个人口味问题。 - Alexander Pogrebnyak
阅读完原帖的第二次编辑后,我认为他正在寻找类级别的面向对象机制,而Java不支持这种机制。您可以使用实例方法进行所有OOP操作,但不能使用(Java中的static)方法。您将不得不(1)接受每个实例都有其自己的变量副本的事实,或者(2)创建一个全局HashMap来包含类名和TAG值之间的映射,以便在每个类的基础上进行。两者都是不完美的,但是嘿,没有人说Java能理解英语。;) - dimitarvp

1

因为没有变量的实现,它不能是抽象的 ;)


1

你为什么想让所有的子类都定义这个变量呢?如果每个子类都应该有它,那就在超类中定义它。顺便说一句,既然良好的面向对象编程实践本来就不应该暴露字段,那么你的问题就更没有意义了。


1

要添加每个类的元数据,也许注释可能是正确的方法。

然而,你不能强制在接口中存在注释,就像你不能强制静态成员或特定构造函数的存在一样。


你能展示一个例子吗?我很想看看这是如何工作的。 - byxor

0
public abstract class Duck 
{
    /** This is not static. */
    private String duckName = "";

    /** Abstract Constructor. */
    public Duck(final String name) 
    {
        duckName = name;
    }

    /** Accessor method. */
    public String getName()
    {
         return duckName;
    }

    /** An example of an abstract method that has to be made concrete. */
    public abstract void fly(); 

}

然后你将拥有另一个类:

public class KhakiCampbell extends Duck
{
    // Constructor
    public KhakiCampbell(final String name)
    {
        super(name);
    }

    @Override
    public void fly()
    {
        /** Do something simple. */
       System.out.println(getName() + " is flying!");
    }
 
    /** This is something that only Khaki Cambell ducks do, of course, joke!. */
    public void crash()
    {
       /** Do something simple. */
       System.out.println(getName() + " has crashed!");
    }
 
    /** Keep going. */
    public void stillFly()
    {
        /** Do something simple. */
        System.out.println(getName() + " is still flying!");
    }

    public static void main(String[] args)
    {
        KhakiCampbell  duck = new KhakiCampbell("Bella");
        KhakiCampbell  mavis = new KhakiCampbell("Mavis");
        KhakiCampbell  boris = new KhakiCampbell("Boris");
        duck.fly();
        mavis.fly();
        boris.crash();
        mavis.stillFly();
    }
}

我将上述代码放入了名为Duck.java和KhakiCampbell.java的不同文件中,然后运行了它。在Eclipse中运行KhakiCampbell后,我在Java控制台上看到了以下内容:
Bella is flying!
Mavis is flying!
Boris has crashed!
Mavis is still flying!

希望这回答了你的问题。你不需要任何静态变量声明。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接