← Java tutorials

LESSON 23 / 40 · FREE TUTORIAL

Behavior Contracts

Abstract class skeleton, interface agreement ability

90–110 minutesThis chapter uses JDK 21 and does not require the preview function.Face Object Progress

WHAT YOU WILL MAKE

Make three ability contracts AbilityContracts

Bats and elytra players are not from the same family, but they can both fly. Use interfaces to stipulate "what will happen", use abstract classes to save the common skeleton, and make the capability contract small and clear. Front-end backpack: inheritance, polymorphism and method overriding. Today we do not form teams based on looks, but sign contracts based on ability.

Before you start: It is recommended to complete Lessons 21–22 first

The common state has been written and the differential behavior is handed over to the subclasses
  1. ActorAbstract common skeleton
  2. name / labelCompleted part
  3. role()Leave it to subclasses to answer

01 / 14

Before starting: predict first, then let the program answer

This chapter uses JDK 21 and does not require the preview function. The story is borrowed from the block world, and the example runs on the ordinary Java console; if the values ​​​​and rules are course models, they will be clearly stated.

Each common example is a complete independent file, saved according to the file name marked on the box. Place each example in a different folder to avoid duplicate names of auxiliary classes. When the complete project is divided into sections, they are connected sequentially in the same file.

Terminal command: replace the Chinese placeholder with the actual file name and class name

javac -encoding UTF-8 ExampleFile.java
java ExampleClass
  • Prediction: Write the expected output first, or draw out the changes in references and data.
  • Verification: Check line by line after running, and then only change one condition.
  • Explanation: Explain whether the results come from Java rules, course contracts, or input data.

02 / 14

Abstract classes leave spaces that must be completed

If all roles have names but there is no unified role implementation, the parent class should not pretend to give a meaningless answer. Abstract classes allow common parts to be written and methods to be explicitly left unfinished.

The common state has been written and the differential behavior is handed over to the subclasses
  1. ActorAbstract common skeleton
  2. name / labelCompleted part
  3. role()Leave it to subclasses to answer

BadAbstract.java · Intentional error example

abstract class Actor {
    protected final String name;
    Actor(String name) { this.name = name; }
    abstract String role();
    public String label() { return role() + " " + name; }
}
public class BadAbstract {
    public static void main(String[] args) { Actor a = new Actor("Not completed"); }
}

03 / 14

Abstract method forces subclass to answer

Abstract methods are like required questions on a test paper. If the subclass is still a concrete class, it must give a method body with a correct signature.

There is no method body but the name parameters and return type are complete
  1. Concrete subclassAbstract methods must be completed
  2. @OverrideVerify method signature
  3. BatProvide bat character

L23Example02.java · Complete independent example

abstract class Actor {
    protected final String name;
    Actor(String name) { this.name = name; }
    abstract String role();
    public String label() { return role() + " " + name; }
}
class Bat extends Actor {
    Bat(String name) { super(name); }
    @Override String role() { return "bat"; }
}
public class L23Example02 {
    public static void main(String[] args) {
        Actor a = new Bat("Night shadow");
        System.out.println(a.label());
    }
}

Output

bat Night shadow

BadRole.java · Intentional error example

abstract class Actor {
    protected final String name;
    Actor(String name) { this.name = name; }
    abstract String role();
    public String label() { return role() + " " + name; }
}
class Bat extends Actor { Bat(String name) { super(name); } }
public class BadRole { public static void main(String[] args) { } }

04 / 14

Is it possible to only describe the interface?

The interface is suitable for agreeing on cross-family capabilities. Bats are animals and elytra players are players, but both can implement Flyable; the caller only needs to know that the other can fly.

The same family is not required, only the same ability is required
  1. FlyableAgreed flight capability
  2. BatRealize flight
  3. ElytraPlayerAlso realize flight

L23Example03.java · Complete independent example

interface Flyable { String fly(); }
class Bat implements Flyable {
    @Override public String fly() { return "Bat flight"; }
}
class ElytraPlayer implements Flyable {
    @Override public String fly() { return "Player gliding"; }
}
public class L23Example03 {
    public static void main(String[] args) {
        Flyable[] team = {new Bat(), new ElytraPlayer()};
        for (Flyable member : team) System.out.println(member.fly());
    }
}

Output

Bat flight
Player gliding

05 / 14

The implementation method must be disclosed

Interface methods are public by default. Implementing classes must provide the same visible access using public.

The interface commitment is a public entrance
  1. Interface abstract methodDefault public
  2. Implementation methodCannot reduce visibility
  3. @OverrideCheck the same contract

BadPublic.java · Intentional error example

interface Flyable { String fly(); }
class Bat implements Flyable { String fly() { return "Flight"; } }
public class BadPublic { public static void main(String[] args) { } }

06 / 14

A class can sign multiple contracts

A Java class can only directly inherit one parent class, but can implement multiple interfaces. A flying bomb can be both Flyable and Explodable.

There is only one parent class for inheritance, and there can be multiple interfaces.
  1. A parent classA class can only directly inherit one
  2. Multiple interfacesCapabilities can be combined
  3. FlyingMineCan fly and detonate at the same time

L23Example05.java · Complete independent example

interface Flyable { String fly(); }
interface Explodable { String explode(); }
class FlyingMine implements Flyable, Explodable {
    @Override public String fly() { return "Fly to target"; }
    @Override public String explode() { return "Detonate"; }
}
public class L23Example05 {
    public static void main(String[] args) {
        FlyingMine mine = new FlyingMine();
        System.out.println(mine.fly());
        System.out.println(mine.explode());
    }
}

Output

Fly to target
Detonate

07 / 14

Interface reference calling ability

If the method parameters are written in the interface type, any implementer can be accepted. This is the expansion of polymorphism from family relationships to capability relationships.

Tool methods only rely on required contracts
  1. Interface parametersAs long as the required capabilities
  2. Any implementerAcceptable same trigger
  3. nullThis lesson agrees to skip safely

L23Example06.java · Section 1/2, sequentially connected to the same file

interface Flyable { String fly(); }
interface Tradable { String trade(String item); }
interface Explodable { int blastPower(); String explode(); }
abstract class Actor {
    protected final String name;
    Actor(String name) { this.name = name; }
    abstract String role();
    public String label() { return role() + " " + name; }
}
class Bat extends Actor implements Flyable {
    Bat(String name) { super(name); }
    @Override String role() { return "bat"; }
    @Override public String fly() { return name + " Fly to high places"; }
}
class Villager extends Actor implements Tradable {
    Villager(String name) { super(name); }
    @Override String role() { return "Villagers"; }
    @Override public String trade(String item) {
        return item == null || item.isBlank()
            ? name + " Reject empty transaction" : name + " Accept" + item.strip();
    }
}
class Creeper extends Actor implements Explodable {
    Creeper(String name) { super(name); }
    @Override String role() { return "Creeper"; }
    @Override public int blastPower() { return 3; }
    @Override public String explode() { return name + " Explosion"; }
}

L23Example06.java · Paragraph 2/2, sequentially connected to the same file

public class L23Example06 {
    static void triggerFlight(Flyable target) {
        if (target != null) System.out.println(target.fly());
    }
    static void triggerTrade(Tradable target, String item) {
        if (target != null) System.out.println(target.trade(item));
    }
    static void triggerExplosion(Explodable target) {
        if (target != null)
            System.out.println(target.explode() + " Strength=" + target.blastPower());
    }
    public static void main(String[] args) {
        triggerFlight(new Bat("Night shadow"));
        triggerTrade(new Villager("Village chief"), "Emerald");
        triggerExplosion(new Creeper("Mung beans"));
        triggerFlight(null);
    }
}

Output

Night shadow Fly to high places
Village chief AcceptEmerald
Mung beans Explosion Strength=3

08 / 14

The small interface is clearer than the universal sundry bag

Packing flying, trading, and explosions into one MegaAbility forces villagers to fly that they can't. Small interfaces are easier to understand and compose.

Each contract revolves around a stable capability
  1. FlyableOnly responsible for flying
  2. TradableOnly responsible for transactions
  3. ExplodableOnly responsible for explosion

L23Example07.java · Complete independent example

interface Flyable { String fly(); }
interface Tradable { String trade(String item); }
interface Explodable { int blastPower(); String explode(); }
abstract class Actor {
    protected final String name;
    Actor(String name) { this.name = name; }
    abstract String role();
    public String label() { return role() + " " + name; }
}
class Bat extends Actor implements Flyable {
    Bat(String name) { super(name); }
    @Override String role() { return "bat"; }
    @Override public String fly() { return name + " Fly to high places"; }
}
class Villager extends Actor implements Tradable {
    Villager(String name) { super(name); }
    @Override String role() { return "Villagers"; }
    @Override public String trade(String item) {
        return item == null || item.isBlank()
            ? name + " Reject empty transaction" : name + " Accept" + item.strip();
    }
}
class Creeper extends Actor implements Explodable {
    Creeper(String name) { super(name); }
    @Override String role() { return "Creeper"; }
    @Override public int blastPower() { return 3; }
    @Override public String explode() { return name + " Explosion"; }
}
public class L23Example07 {
    public static void main(String[] args) {
        Tradable merchant = new Villager("Village chief");
        System.out.println(merchant.trade(""));
    }
}

Output

Village chief Reject empty transaction

09 / 14

Comparison of abstract classes and interfaces

Abstract classes can save instance state, constructors and common implementations; interfaces emphasize the ability that callers can rely on. A class often inherits an abstract parent class and implements several interfaces at the same time.

Family skeleton and capability contracts solve different problems
  1. Abstract classState, constructor, common implementation
  2. InterfaceCaller-dependent capabilities
  3. Use in combinationInherit the skeleton and implement the interface
Acceptance comparison table
ProblemAbstract classInterface
ExpressionSame family skeletonCross-family capabilities
Instance fieldYesDo not save normal instance state
ConstructorYesNo
QuantityOnly one can be inheritedCan realize multiple
Typical exampleActorFlyable

10 / 14

Contract Testing and Failure Clinic

Interface errors are often exposed during the compilation phase, and behavioral errors still need to be discovered through testing.

Compiler checks signature Run test checks behavior
  1. Signature matchingParameters cannot be omitted
  2. Empty transactionExplicitly return rejection
  3. Explosion intensityThis lesson agrees 3

BadTrade.java · Intentional error example

interface Tradable { String trade(String item); }
class Villager implements Tradable {
    public String trade() { return "Transaction"; }
}
public class BadTrade { public static void main(String[] args) { } }
Acceptance comparison table
TestEnterExpected
FlightBatReturn to flight description
TransactionEmpty item nameReturn rejection prompt
ExplosionCreeperStrength is 3
Null referencenullSafety prompt or skip

11 / 14

Challenge Blueprint Three Abilities

The challenge program is called AbilityContracts. Bat can fly, Villager can trade, and Creeper can explode; all three inherit Actor and realize their respective abilities.

Abstract roles provide names and three interfaces provide capability entrances.
  1. BatFlight Contract
  2. VillagerTransaction contract
  3. CreeperExplosion Contract

Acceptance conditions

  • Actor cannot be new directly.
  • All three concrete classes implement role.
  • All interface implementation methods are public.
  • The trigger method uses interface parameters.
  • A rejection prompt is returned when trading an empty string.

12 / 14

Challenges Implementing Capability Contracts

Create AbilityContracts.java, compile and run using Java 21.

Flight transaction explosion is triggered uniformly through the interface
  1. First statementThree interfaces and Actor
  2. ReimplementationComplete specific classes one by one
  3. Post triggerInterface parameters are uniformly called

AbilityContracts.java · Paragraph 1/2, sequentially connected to the same file

interface Flyable { String fly(); }
interface Tradable { String trade(String item); }
interface Explodable { int blastPower(); String explode(); }
abstract class Actor {
    protected final String name;
    Actor(String name) { this.name = name; }
    abstract String role();
    public String label() { return role() + " " + name; }
}
class Bat extends Actor implements Flyable {
    Bat(String name) { super(name); }
    @Override String role() { return "bat"; }
    @Override public String fly() { return name + " Fly to high places"; }
}
class Villager extends Actor implements Tradable {
    Villager(String name) { super(name); }
    @Override String role() { return "Villagers"; }
    @Override public String trade(String item) {
        return item == null || item.isBlank()
            ? name + " Reject empty transaction" : name + " Accept" + item.strip();
    }
}
class Creeper extends Actor implements Explodable {
    Creeper(String name) { super(name); }
    @Override String role() { return "Creeper"; }
    @Override public int blastPower() { return 3; }
    @Override public String explode() { return name + " Explosion"; }
}

AbilityContracts.java · Paragraph 2/2, sequentially connected to the same file

public class AbilityContracts {
static void triggerFlight(Flyable target) {
    if (target != null) System.out.println(target.fly());
}
static void triggerTrade(Tradable target, String item) {
    if (target != null) System.out.println(target.trade(item));
}
static void triggerExplosion(Explodable target) {
    if (target != null)
        System.out.println(target.explode() + " Strength=" + target.blastPower());
}
    public static void main(String[] args) {
        Bat bat = new Bat("Night shadow");
        Villager villager = new Villager("Village chief");
        Creeper creeper = new Creeper("Mung beans");
        System.out.println(bat.label());
        triggerFlight(bat);
        triggerTrade(villager, "Emerald");
        triggerTrade(villager, " ");
        triggerExplosion(creeper);
        triggerFlight(null);
    }
}

Output

bat Night shadow
Night shadow Fly to high places
Village chief AcceptEmerald
Village chief Reject empty transaction
Mung beans Explosion Strength=3

13 / 14

Expansion tasks and clearance review

Each interface must be accompanied by a contract description, clearly stating the input, return value and failure handling.

Three-tier mission expansion capability contract
  1. Contract descriptionWrite clear input and output
  2. Failure RulesExplicitly reject or skip
  3. Compilation and BehaviorBoth inspections are required

Third gear operation

  • Wooden Pickaxe: Designed to be Swimmable so that both dolphins and players can swim.
  • Iron Pick: Let a class implement Flyable and Tradable at the same time, and use two interface references to call.
  • Diamond Pickaxe: Design the redstone device interface, implement doors, lights and traps respectively, and write unified triggers.
Acceptance comparison table
Self-evaluationCanStill need to practice
I can differentiate between abstract classes and interfaces□□
I will use implements□□
I can design small interfaces□□

14 / 14

Own acceptance record

Beautiful output is the starting point, and being able to explain boundaries is considered mastery. Select one case each from normal, borderline and error, write down your expectations before running, and leave evidence after running.

Leave a basis for judgment
CategoryThe operation you selectedCheck key points
NormalA common input or a successful actionIs the result consistent line by line?
BorderRange endpoints and adjacent valuesIs there one more step or one less thing?
ErrorIllegal quantity, missing object or bad dataWhether it is explicitly rejected and whether the status is maintained
  • Bug records: triggering conditions, actual phenomena, causes, and minimum repairs.
  • Illustrated record: describe the starting point, end point and meaning of each arrow.
  • Adaptation record: only change one rule, and modify expectations and tests at the same time.

TAKE IT WITH YOU

Take the lesson offline

The complete lesson, code, diagrams and exercises, in your preferred language.