简体   繁体   中英

Java: How do I write a generic method?

let's say that I have several Creature subclasses, and that they have each have some sort of getGroup() method that returns a List<Creature> .

What I mean by "some sort of" .getGroup() method is that the name of this function varies between subclasses. For instance, Wolf s travel in packs, so they have a getPack() member. Fish travel in schools, so they have a .getSchool() member, Humans have a getFamily() member, and so on.

.getGroup() doesn not exist in Creature , and it cannot be added to the interface. None of these clases can be edited.

I'm writing a method to print the number of Creature s in their group. How would I do this?

Essentially, I'm looking to condense these two functions into the same thing:

    public void PrintSchoolSize(Fish dory) {
        System.out.print(dory.getSchool().size());
    }

    public void PrintHiveSize(Bee bee) {
        System.out.print(bee.getColony().size());
    }

...into the following function:

    public void printGroupSize( Class<? extends Creature> cree, 
                                FunctionThatReturnsList getGroup() ) {
        System.out.print(cree.getGroup().size();
    }

I'd imagine I need to pass in a second argument (function pointer?) to void printGroupSize . Any help is very appreciated, thanks!

EDIT Thank you all for the help. This is just a simplification of the real problem I'm trying to solve. Long, overly complex problems are tougher to answer, so I posed this simpler scenario.

The only answer lies in using a generic function (if that exists). The classes I'm actually working with don't have a common interface, but they all have a function that returns a List.

What you describe in your question is not much related to Java's sense of "generic methods". You could implement it with reflection (see Class.getMethod()), but I promise you that you really don't want to go there.

It would be better for Creature to declare a possibly- abstract method getGroup() that each subclass would override. You may do that in addition to providing methods with subclass-specific names, if you wish. Code that wants to obtain the group (or its size) without knowing the specific type of creature would invoke that creature's getGroup() method. That's an application of polymorphism , which seems to be what you're actually after.

If getGroup cannot be added to the Creature interface why not add another interface to your creatures?

public interface HasGroup {
    Group getGroup();
}

Would mean you can create the method:

public void printGroupSize(HasGroup cree) {
    System.out.print(cree.getGroup().size();
}

The simplest way is to had a getGroup() method to the Creature interface and implement it in each subclass, but it seems you cannot do that.

If you can modify the subclasses, I would actually create a new interface CreatureGroupable with a getGroupSize() and/or getGroup() . Each subclass of Creature shall implement this interface, eg

public interface CreatureGroupable {
   CreatureGroup getGroup();
}

public enum CreatureGroup {
   WOLF_PACK("pack", 30), 
   GEES_FLOCK("flock", 20),
   FISH_SCHOOL("school", 1000),
   HUMAN_FAMILY("family", 4),
   ...
   private final String name;
   private final int size;

   private CreatureGroup(String name, int size) {
     this.name = name;
     this.size = size; 
   }

   public String getName() { return name; }
   public int getSize() { return size; }
}

public class Wolf implements Creature, CreatureGroupable {
   // methods from Creature, constructor, ...

   public CreatureGroup getGroup() {
      return CreatureGroup.WOLF_PACK;
}

This way, if you have a List<Creature> you can access the group of each one and do whatever you have to do, eg

public void printGroups(List<Creature> creatures) {
   for (Creature c : creatures) {
      CreatureGroup group = c.getGroup();
      System.out.println("A " + group.getName() + 
                         " has roughly " group.getSize() + 
                         " individuals.");    
   }
}

If you want more flexibility, you may not use an enum and just a standard interface and class hierarchy for the groups.

Thanks to everyone for the help. Since I'm not allowed to edit any of the aforementioned classes/interfaces (I can only write external functions), I wrote the following function

  public List<? extends Creature> getGroup(Object obj) {

      if(obj.getClass() == Bee.class)
          return ((Bee)obj).getColony();

      if(obj.getClass() == Fish.class)
          return ((Fish) obj).getSchool();

      /* repeat for the other classes */

      return null;
  }

...and used it here, as so:

   public void printGroupSize( Class<? extends Creature> cree ) {
       System.out.print(getGroup(cree).size());
   }

I have verified that this solution does indeed work, since all of the get*****() functions return a List<Creature> . This solution also shrinks my codebase significantly, and is easier to maintain than the current structure.

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