强制使对象字段仅具有从父类继承的特定值。

huangapple go评论75阅读模式
英文:

Force an object field to having only certain values which are inherited from parent class

问题

我有一个抽象类 Pet,里面有一个抽象方法 feed(...)。这个方法只能接受三种特定类型的食物之一(DRY、WET 或 RAW)。它们可以是字符串类型,因为我只在以后的逻辑中将它们用作开关中的指示器。另外,我有两个子类 CatDog。它们从 Pet 类中实现了 feed(...) 方法。我尝试过使用枚举来解决:

Pet.java:

public abstract class Pet {

    public enum FoodType {
        DRY,
        WET,
        RAW;
    }

    public abstract void feed(FoodType foodType);
}

Cat.java:

public class Cat extends Pet {

    public void feed(FoodType foodType) {
        switch (foodType) {
            case DRY:
                System.out.println("给这只猫喂干粮。");
                break;
            case RAW:
                System.out.println("给这只猫喂生肉。");
                break;
            case WET:
                System.out.println("给这只猫喂湿粮。");
                break;
        }
    }
}

Dog.java:

public class Dog extends Pet {

    public void feed(FoodType foodType) {
        // 与 Cat 中的 feed() 方法类似的实现。
    }
}

App.java:

import pets.Dog;
import pets.Pet;

public class App {

    public static void main(String[] args) {
        Dog dog01 = new Dog();
        dog01.feed(Pet.FoodType.DRY);
    }
}

但我不喜欢这样,因为默认情况下枚举是静态的,FoodType 可以从项目中的任何地方访问。在创建 CatDog 对象之前,我可以从任何地方调用 Pet.FoodType.DRY。最好的情况是它只与继承自 Pet 的对象关联,例如:

Dog dog01 = new Dog();
dog01.FoodType.DRY; // 对这个做点什么
英文:

I have abstract class Pet with abstract method feed(...). The method must take only on of certain three types of food (DRY, WET or RAW). These can be String type as I use them only as indicators in SWITCH in later logic. Also I have two subclasses Cat and Dog. They implement the feed(...) method from Pet. The closest I came to, is using ENUM:
Pet.java:

public abstract class Pet {

    public enum FoodType {
        DRY,
        WET,
        RAW;
    }

    public abstract void feed(FoodType foodType);
}

Cat.java:

public class Cat extends Pet {

    public void feed(FoodType foodType) {
        switch (foodType) {
            case DRY:
                System.out.println("Feeding this cat dry food.");
                break;
            case RAW:
                System.out.println("Feeding this cat raw food.");
                break;
            case WET:
                System.out.println("Feeding this cat wet food.");
                break;
        }
    }
}

Dog.java:

public class Dog extends Pet {

    public void feed(FoodType foodType) {
        // implementation similar to feed() in Cat.
    }
}

App.java:

import pets.Dog;
import pets.Pet;

public class App {

    public static void main(String[] args) {
        Dog dog01 = new Dog();
        dog01.feed(Pet.FoodType.DRY);
    }
}

But I don't like that, since ENUMS are static by default FoodType can be accessed from anywhere in project. Without creating a Cat or Dog object, i can call Pet.FoodType.DRY from anywhere. Preferebly it should be associated only with an object that inherits from Pet, eg:

Dog dog01 = new Dog();
dog01.FoodType.DRY; // do smth with this

答案1

得分: 1

这是因为您已将FoodType声明为public。请将其替换为protected,这样问题就会得到解决。

protected enum FoodType

或者,为了解决这个问题,我们可以将其实现为业务逻辑。

public abstract class Pet {

    protected String[] foodType = new String[]{"RAW", "DRY", "WET"};

    protected final String RAW_FOOD_TYPE = "RAW";
    protected final String DRY_FOOD_TYPE = "DRY";
    protected final String WET_FOOD_TYPE = "WET";

    public abstract void feed(String foodType);

    public String getRawFoodType() {
        return RAW_FOOD_TYPE;
    }

    public String getDryFoodType() {
        return DRY_FOOD_TYPE;
    }

    public String getWetFoodType() {
        return WET_FOOD_TYPE;
    }
}

public class Cat extends Pet {

    public void feed(String foodType) {
        if (foodType.equals(DRY_FOOD_TYPE)) {
            System.out.println("Feeding this cat dry food.");
        } else if (foodType.equals(RAW_FOOD_TYPE)) {
            System.out.println("Feeding this cat raw food.");
        } else if (foodType.equals(WET_FOOD_TYPE)) {
            System.out.println("Feeding this cat wet food.");
        } else {
            throw new RuntimeException("Invalid foodtype");
        }
    }
}

public class App {

    public static void main(String[] args) {
        Cat cat01 = new Cat();
        cat01.feed(cat01.getDryFoodType());
    }
}
英文:

This is happening because you have declared FoodType as public. Please replace that with protected and this issue will be resolved.

protected enum FoodType

Alternatively to resolve this issue probably we can implement that as business logic

public abstract class Pet {
protected String[] foodType = new String[]{"RAW","DRY","WET"};
protected final String RAW_FOOD_TYPE = "RAW";
protected final String DRY_FOOD_TYPE = "DRY";
protected final String WET_FOOD_TYPE = "WET";
public abstract void feed(String foodType);
public String getRawFoodType()
{
return RAW_FOOD_TYPE;
}
public String getDryFoodType()
{
return DRY_FOOD_TYPE;
}
public String getWetFoodType()
{
return WET_FOOD_TYPE;
}
}
public class Cat extends Pet {
public void feed(String foodType) 
{
if(foodType.equals(DRY_FOOD_TYPE))
{
System.out.println("Feeding this cat dry food.");
}
else if(foodType.equals(RAW_FOOD_TYPE))
{
System.out.println("Feeding this cat raw food.");
}
else if(foodType.equals(WET_FOOD_TYPE))
{
System.out.println("Feeding this cat wet food.");
}
else
{
throw new RuntimeException("Invalid foodtype");
}
}
}
public class App {
public static void main(String[] args) {
Cat cat01 = new Cat();
cat01.feed(cat01.getDryFoodType());
}
}
```
</details>
# 答案2
**得分**: 0
通常在实践中,将枚举类型声明为公共(public)是可以的。但是回答你的问题,通过稍微修改结构是可以实现的。在这里,这些常量可以作为 ```enum``` 或者 ```private final string```,两者都可以。
以枚举类型为例,将其声明为受保护(protected)。然后在 ```Pet``` 类内部创建一个方法,该方法将返回枚举值,实际上就是将其封装在一个 getter 中。
```java
public FoodType getFoodType(String foodTypeStr){
return FoodType.valueOf(foodTypeStr);
}
```
这将确保只有子类的实例变量可以调用它。因此,你可以这样做:
```java
dog01.feed(dog01.getFoodType("DRY"));
```
如果需要的话,这可以被设置为受保护的,并在子类中进行覆盖。
<details>
<summary>英文:</summary>
So usally it should be fine to have public enum in practise for this. But to answer your question yes it is possible by modifying the structure a bit. So the constants here could be as ```enum``` or as ```private final string``` both would be fine. 
Taking this case for enum, declare it as protected. Then create a method which will return the enum value inside the ```Pet``` class itself , which is essentially wrapping this in a getter. 
```
public FoodType getFoodType(String foodTypeStr){
return FoodType.valueOf(foodTypeStr);
}
```
This will make sure only instance variables of child classes can invoke it. Hence you can do something like this, 
```
dog01.feed(dog01.getFoodType(&quot;DRY&quot;));
```
This can be made protected and ovveriden in child if needed.
</details>

huangapple
  • 本文由 发表于 2020年4月4日 01:14:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/61017072.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定