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 framework such as Jakarta Contexts and Dependency Injection, Google Guice, or Spring. 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. 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.
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 by baeldung. And read Question, Can an abstract class have a constructor?.
- 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. 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.
- For simplicity, I replaced your multiple member fields on
PrintAd
to a single LocalDate
.
- 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:
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 "Advertisement[" +
"id=" + id + ", " +
"description=" + description + ']';
}
}
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 < Advertisement > ads = null;
try
{
ads =
List.of (
new PrintAd (
UUID.fromString ( "a71330e7-131f-4d10-beac-4e8ffc21887f" ) ,
"Example print ad." ,
LocalDate.now ( )
) ,
new WebAd (
UUID.fromString ( "8aafa2cf-03dc-452b-a84d-c61d75964955" ) ,
"Example web ad." ,
URI.create ( "https://www.DailyBugle.com/ads" ).toURL ( )
) ,
new RadioAd (
UUID.fromString ( "e21b187c-01e2-44da-9a26-5debb9146cf6" ) ,
"Example radio ad." ,
LocalTime.NOON
)
);
}
catch ( MalformedURLException e ) { throw new RuntimeException ( e ); }
System.out.println ( "ads = " + 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: