Search code examples
javainner-classes

Java method that requires instance of enclosing class but not inner class


If I have a non-static inner class, I know that it cannot contain static methods, because it requires an instance of the enclosing class in order to be accessible. However, is there any way to create a method that can be referenced with an instance of the outer class but not the inner one? Something along the lines of:

public class Family {

  public final Set<Dog> pets = new HashSet<Dog>();

  public class Dog {
    
    public Color color;
    public int weight;
    public int height;

    public Dog(Color color, int weight, int height) {
      this.color = color;
      this.weight = weight;
      this.height = height;
      Family.this.pets.add(this);
    }

    public void addDefaultBlack() {
        new Dog(Color.BLACK, 10, 10);
    }

    public void addDefaultWhite() {
        new Dog(Color.WHITE, 5, 5);
    }

  }

}

In such a way that I can reference it something like:

Family family = new Family();
family.Dog.addDefaultBlack();

...by requiring an instance of the outer class, but not the inner one. In a case such as this, Dog is not a static inner class because each dog must belong to a family, but I want some methods that could create dogs with some default properties and add it to the Family object of the enclosing class. The only way I could think to do this currently is to create an enum (such as something like DefaultDogs) and pass it as an argument in a new constructor, then switch-case it to apply the properties. That seems somewhat messy, so I'd prefer to avoid it. Is there a way to do this with methods as I've shown? What's the correct syntax? Sorry if I'm missing something obvious here.

EDIT: I'm aware I could put a method in the outer class, but for readability and logic it makes more sense to me to keep it in the inner class in my opinion. Is that the only way to make this work?


Solution

  • The following will do what you want. But you will need to pass an instance of the Family class to the method to be used to create a new instance of the Dog class. The addDefault classes can be declared static so they can be indirectly accessed vi Family.Dog.

    import java.awt.Color;
    import java.util.HashSet;
    import java.util.Set;
    
    public class FamilyDemo {
        public static void main(String[] args) {
            Family family1 = new Family();
            Family family2 = new Family();
            family1.new Dog(Color.orange, 70,40);
            family2.new Dog(Color.green, 100,200);
            
            Family.Dog.addDefaultBlack(family1);
            Family.Dog.addDefaultWhite(family2);
            System.out.println(family1.pets);
            System.out.println(family2.pets);
        }
        
    }
    
    class Family {
        
        public final Set<Dog> pets = new HashSet<>();
        public class Dog {
            
            public Color color;
            public int weight;
            public int height;
            
            public Dog(Color color, int weight, int height) {
                this.color = color;
                this.weight = weight;
                this.height = height;
                pets.add(this);
            }
            
            
            public static void addDefaultBlack(Family instance) {
                instance.new Dog(Color.BLACK, 10, 10);
            }
            
            public static void addDefaultWhite(Family instance) {
                instance.new Dog(Color.WHITE, 5, 5);
            }
            
            public String toString() {
                return color + ", " + weight + ", " + height;
            }
        }
    }
    

    I thought about this some more and have a few ideas for you to consider.

    • Unless you are planning on drawing the pets, don't use Color. It is not use friendly for descriptive purposes. Create an enum for the Color and require it be used as an argument in the constructors. You can always add more colors and not affect someones existing implementation.
    • Change Dog class to Pet class. Then you can have an enum of possible pets. You can even have the enum types provide ranges for the pets to enforce invariants in the values. For example, if you allow a tarantula as a pet you wouldn't want to allow it to be 100 lbs. (yikes!). More pets can be added by simply adjusting the enum. Min and max values and others can all be specified as arguments to the enum types.
    • Finally, not only Families can have pets, but single people and even organizations. The root of your initial problem was having the inner class add an instance of dog to the enclosing class's Dog set. Why not just have a Pet class external to Family, useful by others. You could make it a PetFactory with a custom setting as well as standard default pets. Then leave it up to the using class to add the pet to the set.