In Java, how can I create a class whose methods are different in each instance?

128 views Asked by At

I'm working on a basic game AI, and I'm trying to encapsulate the transitions between nodes in a behavior tree using an abstract class called Transition.

Right now, Transition looks like this:

package com.game.behaviors;

public abstract class Transition {
    public abstract Node getNextNode();
}

What I'd like to be able to do is to specify the logic inside the method getNextNode on each instance of Transition. Some transitions, for example, will always return the same Node, while others may return one of several nodes based on either a random number generator or on some other logic.

I know that I can achieve this by creating a class that inherits from Transition for each behavior I want, and then getting instances of those classes, like so:

class RealTransition extends Transtition{
    Node thisNode = someNode;

    public Node getNextNode(){
        return thisNode;
    }
}

RealTransition rt = new RealTransition();
someObject.someMethodThatWantsATransition(rt);

As a Java noob that mostly works in Javascript, this feels clunky. Creating a new class that I know I'm only going to instantiate once feels like it should be unneccesary, especially since I'm probably going to define lots of transitions. Is there a better way I can go about defining how my transitions work?

2

There are 2 answers

0
Bifz On BEST ANSWER

As @ajb stated, you can do the same thing using Functional Interfaces from Java 8. It is basically the same, but you use anonymous classes to pass your desired behavior.

Define your Transition like this:

@FunctionalInterface
public interface Transition {
    public Node getNextNode();
}

Then use it in your method with a lambda expression:

someObject.someMethodThatWantsATransition(() -> {
    // your method body here;
});

Since any Functional Interface can only have one method, a new Transition is instantiated and it's only existing method is called.

1
Andrew Lazarus On

One of Java's neatest features is being able to make each value of an enum behave differently. Basically, each value is a singleton object inheriting from the parent enum. This sounds like a match for what you are doing.

See the Constant-specific methods in Thinking in Java for details. It starts on page 740 at that PDF.

[EDIT: Example copied from link above. This is a good pattern when the nodes of the tree (e.g., character types in a game) are fixed in advance. It doesn't work when applied to objects created on the fly.]

//: enumerated/CarWash.java
import java.util.*;
import static net.mindview.util.Print.*;
public class CarWash {
 public enum Cycle {
 UNDERBODY {
 void action() { print("Spraying the underbody"); }
 },
 WHEELWASH {
 void action() { print("Washing the wheels"); }
 },
 PREWASH {
 void action() { print("Loosening the dirt"); }
 },
 BASIC {
 void action() { print("The basic wash"); }
 },
 HOTWAX {
 void action() { print("Applying hot wax"); }
 },
 RINSE {
 void action() { print("Rinsing"); }
 },
 BLOWDRY {
 void action() { print("Blowing dry"); }
 };
 abstract void action();
 }
 EnumSet<Cycle> cycles =
 EnumSet.of(Cycle.BASIC, Cycle.RINSE);
 public void add(Cycle cycle) { cycles.add(cycle); }
 public void washCar() {
 for(Cycle c : cycles)
 c.action();
 }
 public String toString() { return cycles.toString(); }
 public static void main(String[] args) {
 CarWash wash = new CarWash();
 print(wash);
 wash.washCar();
 // Order of addition is unimportant:
 wash.add(Cycle.BLOWDRY);
 wash.add(Cycle.BLOWDRY); // Duplicates ignored
 wash.add(Cycle.RINSE);
 wash.add(Cycle.HOTWAX);
 print(wash);
 wash.washCar();
 }
} /* Output:
[BASIC, RINSE]
The basic wash
Rinsing
[BASIC, HOTWAX, RINSE, BLOWDRY]
The basic wash
Applying hot wax
Rinsing
Blowing dry