有没有办法不必为每个子类都编写变量和构造函数?

huangapple go评论53阅读模式
英文:

Is there a way to not have to write the variables and the constructor for every child class

问题

以下是您要翻译的内容:

创建一个管理广告的程序。每个广告都有2个共同属性:1:ID,2:描述。
根据广告是用于网页还是报纸等,它将具有不同的每页成本、字数或显示持续时间。

我有3个文件

  1. Main.java
  2. Advertisement.java
  3. PrintedAd.java
  4. WebpageAd.java

Advertisement.java

public class Advertisement{

    private int ID;
    private String description;

    // 构造函数
    public Advertisement(int ID, String description){
        this.ID = ID;
        this.description = description;
    }
    public int getID() {
        return ID;
    }

PrintedAd.java

public class PrintedAd extends Advertisement{

        private int First;   //第一页成本
        private int Middle;  //中间页成本
        private int Last;    //最后一页成本

    public PrintedAd(int First, int Middle, int Last){
        this.First = First;
        this.Middle = Middle;
        this.Last = Last;
    }
    public int getFirst() {
        return First;
    }

Main.java

public class Main{
    public static void main(String[] args) {
        
        Advertisement ad = new Advertisement(34567, "test");
        System.out.println(ad.getID());

        PrintedAd ad2 = new PrintedAd(10,20,30);
        System.out.println(ad2);
    }
}

(为了更易读,我不得不更改变量名称,所以如果出现任何“愚蠢”的错误,请理解)
从这两个类创建2个不同的变量是有效的。

尽管变量ad2是一则广告,需要有一个ID号和一个描述,但我无法同时拥有Advertisement和PrintedAd属性。

我正在询问的是是否有办法不必为每个子类编写变量和构造函数。
如果我要为每种类型的广告都添加这个,我认为这将解决问题,但还是要问是否有办法:

    private int ID;
    private String description;

    // 构造函数
    public Advertisement(int ID, String description){
        this.ID = ID;
        this.description = description;
    }
英文:

Creating a program that manages advertisements. Every ad will have 2 common attributes 1: ID, 2: Description.
Depending if the ad is for a webpage or for a newspaper for example it will have different costs per page, words or duration it is shown.

I have 3 files

  1. Main.java
  2. Advertisement.java
  3. PrintedAd.java
  4. WebpageAd.java

Advertisement.java

public class Advertisement{

    private int ID;
    private String description;

    // Constructor
    public Advertisement(int ID, String description){
        this.ID = ID;
        this.description = description;
    }
    public int getID() {
        return ID;
    }

PrintedAd.java

public class PrintedAd extends Advertisement{

        private int First;   //first page cost
        private int Middle;  //middle page cost
        private int Last;    //last page cost

    public PrintedAd(int First, int Middle, int Last){
        this.First = First;
        this.Middle = Middle;
        this.Last = Last;
    }
    public int getFirst() {
        return First;
    }

Main.java

public class Main{
    public static void main(String[] args) {
        
        Advertisement ad = new Advertisement(34567, "test");
        System.out.println(ad.getID());

        PrintedAdd ad2 = new PrintedAdd(10,20,30);
        System.out.println(ad2);
    }
}

(had to change var names to make it more readable so if any "dumb" errors appear please understand)
Creating 2 different variables from these 2 classes works fine.

Even though the variable ad2 is an ad and needs to have an ID number and a description I am unable to do both the Advertisement and PrintedAd attributes.

What I am asking is if there a way for me to not have to write the variables and the constructor for every child class.
If I were to add to every type of ad this it would solve the issue I believe but still asking if there is a way:

    private int ID;
    private String description;

    // Constructor
    public Advertisement(int ID, String description){
        this.ID = ID;
        this.description = description;
    }

答案1

得分: 3

在你的代码中:

PrintedAdd ad2 = new PrintedAdd(10,20,30);

这个新的 PrintedAdd 对象(也是一个 Advertisement)在其两个继承成员字段 IDdescription 中都有 null 值。你忘了调用构造函数(或者设置器)来指定这些值,所以它们默认为 null

所以,如果你希望子类在运行时具有5个字段,其中2个是继承的,3个在子类自身定义,你需要传递5个值(原始类型或对象引用)。你可以通过构造函数传递,和/或者通过设置器方法传递。或者你可以使用依赖注入框架,比如 Jakarta Contexts and Dependency Injection,Google Guice,或者 Spring。否则,成员字段将使用默认值;对于对象引用,这意味着 null

在你的特定情况下,你的对象是只读的,有获取器但没有设置器。所以这就排除了设置器的选项。而且在你的情况下,依赖注入框架不适用,因为它是用来提供资源和服务的,而不是用来提供单个值,比如ID或描述。所以,只剩下构造函数的选项。你应该编写子类以接受5个参数的构造函数,包括2个继承字段和3个子类特定字段。

请注意,子类的构造函数可以调用父类的构造函数。可以参考现有的问题,比如 "在Java中从子类构造函数调用父类"。

以下是一些示例代码和注释。

  • 你的超类应该被标记为 abstract,因为你只打算实例化子类,而不是超类。你可能想阅读由 baeldung 提供的 "Java抽象类中的构造函数"。还可以阅读问题, "抽象类可以有构造函数吗?"。
  • 由于你在编译时知道所有的子类,并且不希望在运行时动态添加新的子类,你可以封闭超类。封闭的类和接口限制了哪些其他类或接口可以扩展或实现它们。(对于你的问题不重要,只是现代Java编程的一些注意事项。)
  • 在Java的命名约定中,成员字段应该以小写字母开头命名。所以,应该是 first,而不是 First。应该是 id,而不是 ID。按照约定,全部大写表示一个常量。
  • 为了简化,我将 PrintAd 上的多个成员字段替换为一个 LocalDate
  • 注意,将id和描述传递给每个构造函数是有意义的,因为没有其他方法来确定这些字段的值应该是什么。

超类:

// 在这里是你的超类代码

每个子类,还有一个额外的 RadioAd

// 在这里是你的子类代码

一个应用程序来使用这些业务逻辑类:

// 在这里是你的应用程序代码

运行时:

// 在这里是运行时输出

要了解更多信息,请查看Oracle提供的免费的 Java 教程:

  • 控制类的成员访问
  • 继承

<details>
<summary>英文:</summary>

In your code:

    PrintedAdd ad2 = new PrintedAdd(10,20,30);

This new `PrintedAdd` object (also an `Advertisement`) has `null` in its two inherited member fields, `ID` and `description`. You neglected to invoke a constructor (or setters) to specify those values, so they default to `null`.  

So yes, if you want a subclass to have 5 fields, 2 inherited, and 3 defined on the subclass itself, at run time you need to pass 5 values (primitives or object references). You can pass to a constructor, and/or you can pass via setter methods. Or you could use a [dependency injection][1] framework such as [*Jakarta Contexts and Dependency Injection*][2], [*Google Guice*][3], or [*Spring*][4]. Otherwise, you get default values in member field; for object references that means `null`. 

In your particular case, your objects are read-only, with getters but no setters. So that eliminates the setters option. And in your case, a dependency injection framework is not appropriate, as that is for providing resources and services, not for individual values such as an ID or description. So, that leaves the constructor option. &#128073; You should write the subclass to have a constructor taking 5 arguments, for the 2 inherited fields and the 3 subclass-specific fields.

Be aware that a subclass’ constructor can call a superclass’ constructor. See existing Question such as [*Calling superclass from a subclass constructor in Java*](https://stackoverflow.com/q/19326229/642706). 



Here is some example code, and some notes.

- Your superclass should be marked `abstract` as you intend only to instantiate the subclasses, never the superclass. You may want to read [*Constructors in Java Abstract Classes*](https://www.baeldung.com/java-abstract-classes-constructors) by [baeldung][5]. And read Question, [*Can an abstract class have a constructor?*](https://stackoverflow.com/q/260666/642706). 
- Since you know all subclasses at compile time, and do not expect new ones to be dynamically added at runtime, you can [seal the superclass][6]. Sealed classes and interfaces restrict which other classes or interfaces may extend or implement them. (Not important for your Question, just a note for modern Java programming.)
- In Java naming conventions, a member field should be name with an initial lowercase letter. So, `first`, not `First`. And `id`, not `ID`. All uppercase by convention means a [constant][7]. 
- For simplicity, I replaced your multiple member fields on `PrintAd` to a single `LocalDate`. 
- &#128073; Note how it makes sense to pass the id and description in each constructor — there is no other way to discern what the value should be for those fields.

The superclass:

```java
package work.basil.example.advertising;

import java.util.Objects;
import java.util.UUID;

public abstract sealed class Advertisement
        permits PrintAd, WebAd, RadioAd
{
    // Member fields. Read-only, via getter methods.

    private final UUID id;
    private final String description;

    // Constructors

    public Advertisement ( final UUID id ,
                           final String description )
    {
        this.id = id;
        this.description = description;
    }

    // Getters

    public UUID getId ( )
    {
        return id;
    }

    public String getDescription ( )
    {
        return description;
    }

    // `Object` overrides

    @Override
    public boolean equals ( Object o )
    {
        if ( this == o ) return true;
        if ( o == null || getClass ( ) != o.getClass ( ) ) return false;
        Advertisement that = ( Advertisement ) o;
        return Objects.equals ( id , that.id );
    }

    @Override
    public int hashCode ( )
    {
        return Objects.hash ( id );
    }

    @Override
    public String toString ( )
    {
        return &quot;Advertisement[&quot; +
                &quot;id=&quot; + id + &quot;, &quot; +
                &quot;description=&quot; + description + &#39;]&#39;;
    }

}

Each of the subclasses, with a third one RadioAd just for fun:

package work.basil.example.advertising;

import java.time.LocalDate;
import java.util.UUID;

public final class PrintAd extends Advertisement
{
    private final LocalDate runDate;

    public PrintAd ( final UUID id , final String description , final LocalDate runDate )
    {
        super ( id , description );
        this.runDate = runDate;
    }

    public LocalDate getRunDate ( )
    {
        return runDate;
    }
}
package work.basil.example.advertising;

import java.net.URL;
import java.util.UUID;

public final class WebAd extends Advertisement
{
    private final URL url;

    public WebAd ( final UUID id , final String description , final URL url )
    {
        super ( id , description );
        this.url = url;
    }

    public URL getUrl ( )
    {
        return url;
    }
}
package work.basil.example.advertising;

import java.time.LocalTime;
import java.util.UUID;

public final class RadioAd extends Advertisement
{
    private final LocalTime airTime;

    public RadioAd ( final UUID id , final String description , final LocalTime airTime )
    {
        super ( id , description );
        this.airTime = airTime;
    }

    public LocalTime getAirTime ( )
    {
        return this.airTime;
    }
}

And an app to exercise those business logic classes.

package work.basil.example.advertising;

import java.net.MalformedURLException;
import java.net.URI;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;
import java.util.UUID;

public class App
{
    public static void main ( String[] args )
    {
        App app = new App ( );
        app.demo ( );
    }

    private void demo ( )
    {
        List &lt; Advertisement &gt; ads = null;
        try
        {
            ads =
                    List.of (
                            new PrintAd (
                                    UUID.fromString ( &quot;a71330e7-131f-4d10-beac-4e8ffc21887f&quot; ) ,
                                    &quot;Example print ad.&quot; ,
                                    LocalDate.now ( )
                            ) ,
                            new WebAd (
                                    UUID.fromString ( &quot;8aafa2cf-03dc-452b-a84d-c61d75964955&quot; ) ,
                                    &quot;Example web ad.&quot; ,
                                    URI.create ( &quot;https://www.DailyBugle.com/ads&quot; ).toURL ( )
                            ) ,
                            new RadioAd (
                                    UUID.fromString ( &quot;e21b187c-01e2-44da-9a26-5debb9146cf6&quot; ) ,
                                    &quot;Example radio ad.&quot; ,
                                    LocalTime.NOON
                            )
                    );
        }
        catch ( MalformedURLException e ) { throw new RuntimeException ( e ); }

        System.out.println ( &quot;ads = &quot; + ads );
    }
}

When run:

>ads = [Advertisement[id=a71330e7-131f-4d10-beac-4e8ffc21887f, description=Example print ad.], Advertisement[id=8aafa2cf-03dc-452b-a84d-c61d75964955, description=Example web ad.], Advertisement[id=e21b187c-01e2-44da-9a26-5debb9146cf6, description=Example radio ad.]]


To learn more, see the The Java Tutorials provided by Oracle Corp free of cost:

答案2

得分: -1

以下是您要翻译的内容:

在完成此任务的一种方法是在Advertisement类内创建一个静态字段。

然后,在您的Advertisement构造函数中,将此字段递增1,并将其赋值给您的ID字段。

考虑以下代码:

public class Advertisement {
    private static int universalId = 0;

    private int ID;
    private String description;

    protected Advertisement() {
        ID = universalId++;
    }

    public Advertisement(String description) {
        this();
        this.description = description;
    }

    public int getID() {
        return ID;
    }
}

然后,对于您的子类,在其构造函数内调用超类构造函数,如下所示:

public class PrintedAdd extends Advertisement {
    private int First;   //first page cost
    private int Middle;  //middle page cost
    private int Last;    //last page cost

    public PrintedAdd(int First, int Middle, int Last) {
        super();
        this.First = First;
        this.Middle = Middle;
        this.Last = Last;
    }

    public int getFirst() {
        return First;
    }
}

因此,调用ad.getID()应该产生0,调用ad2.getID()应该产生1

英文:

One way to complete this task is to create a static field within the Advertisement class.

Then, within your Advertisement constructor, increment this field by 1, and assign it to your ID field.

Consider the following.

public class Advertisement {
    private static int universalId = 0;

    private int ID;
    private String description;

    protected Advertisement() {
        ID = universalId++;
    }

    public Advertisement(String description) {
        this();
        this.description = description;
    }

    public int getID() {
        return ID;
    }
}

Then, for your sub-classes, within their constructors', make a call to the super constructor, as follows.

public class PrintedAdd extends Advertisement {
    private int First;   //first page cost
    private int Middle;  //middle page cost
    private int Last;    //last page cost

    public PrintedAdd(int First, int Middle, int Last) {
        super();
        this.First = First;
        this.Middle = Middle;
        this.Last = Last;
    }

    public int getFirst() {
        return First;
    }
}

Thus, making a call to ad.getID() should produce 0, and making a call to ad2.getID() should produce 1.

huangapple
  • 本文由 发表于 2023年5月18日 03:44:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76275687.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定