简体   繁体   中英

How to make a Java class that can take parameters of any type

I'm making a plugin that handles Quests in a game. I have a Quest class that is the instance of a Quest, this is attached to the player's object. I can get the Quest, check it's objectives, which are complete and which aren't etc.

I have four types of Objectives (Talk, Kill Entity, Collect Item, Craft Item). This is stored in an enum. I'm trying to figure out how I can make my Objective class as efficiently as possible, this would require me to init the Objective class with the objective type then the target (the NPC to talk to or the item to collect etc.) but the target would be of a different type depending on the ObjectiveType.

eg: Objective newObjective = new Objective(ObjectiveType.TALK, NPC(10)); would create an objective telling the player to talk to the NPC with the ID 10

Objective newObjective = new Objective(ObjectiveType.COLLECT_ITEM, Item(ZOMBIE_ESSENCE)); would create an objective telling the player to collect one Zombie Essence.

Any help is much appreciated.

Enums cannot be have their own generic types yet (I think there is a JSR for that though). That means that you cannot really tie the ObjectiveType and its argument together. Otherwise, you could have made Objective generic:

// this is not possible yet
enum ObjectiveType<T> {
    TALK<NPC>,
    COLLECT_ITEM<Item>,
    ...
}

class Objective<T> {
    public Objective(ObjectiveType<T> type, T argument) { ... }
}

However, what you can easily do is make the constructor(s) private and use factory methods:

public static Objective newTalkObjective(NPC npc) {
    return new Objective(ObjectiveType.TALK, npc);
}

public static Objective newCollectItemObjective(Item item) {
    return new Objective(ObjectiveType.COLLECT_ITEM, item);
}

Assuming that NPC and item are classes I can think of a number of ways to do this: Create multiple constructors:

public Objective(NPC npc){
...
}

public Objective(Item item){
...
}

The constructor used will also indicate the type of objective. So, no need to pass that in.

A second way would be to make NPC and Item extend a base class. In this way you will only need one constructor for Objective with the second parameter being the of the type of the base class.

But, IMHO, the correct way to go is actualy to have Objective as a base class and extend it for each type of objective. This will make the code MUCH clearer. Any shared values and methods would go in the base class and the specific ones per objective type would go in the child class.

It will also make it much easier to add more objective types in the future because all you have to do is create another class that extends Objective.

idea 1 - sub-class of Objective

Any Objective instance is very similar to a Event instance.

A common design of EventBus is use a sub-class tree to describe any EventType s.

This should be efficient if you take enough abstraction of whole Objective module: BaseObjective , EntityObjective , MonsterObjective , KillMonsterObjective ...

class Objective
{
  ObjectiveType type;
  public Objective(ObjectType type) { this.type = type; }
}

class ObjectiveKillTarget extends Objective
{
  TargetType typeTarget; // any extra parameters
  public ObjectiveKillTarget(TargetType typeTarget)
  {
    super(ObjectiveType.KILL_TARGET);
    this.typeTarget = typeTarget;
  }
}

idea 2 - store key-value-pair in Map<String, ?> or something like that

If you DO want to store all data into instances of ONE class, Map<String, ?> could help.

It is not recommended but works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM