简体   繁体   English

如果要使用子类型的属性,如何防止显式转换?

[英]How can i prevent explicit casting if you want to use properties of the sub-type?

So the compiler complains when ever i do a explicit cast. 因此,编译器会在我进行显式转换时抱怨。 I can prevent this by using a @SuppressWarnings annotation. 我可以通过使用@SuppressWarnings注释来防止这种情况。 At this point i would have this annotation a lot in my code which lets me suspect that there is another way i'm just not aware of. 在这一点上,我的代码中会有很多注释,这让我怀疑还有另一种我不知道的方法。

Lets have a look at this example 让我们看看这个例子

class CutePet
{
    public void pet()
    {
        System.out.println( "The cute pet gets some pets" );
    }
}

class Cat extends CutePet
{
    public void letOutside()
    {
        System.out.println( "The cat goes outside" );
    }


    public void letInside()
    {
        System.out.println( "The cat comes inside" );
    }


    public void removeTick()
    {
        System.out.println( "The cat looses all ticks" );
    }
}

class Dog extends CutePet
{
    public void goForAWalk()
    {
        System.out.println( "The Dog goes for a walk" );
    }


    public void tellHimWhatHeIs()
    {
        System.out.println( "The Dog is a good boy" );
    }
}

class caretaker
{
    public void takeCare( CutePet pet )
    {
        if( pet instanceof Cat )
        {
            pet.pet();
            ((Cat)pet).letOutside();
            ((Cat)pet).letInside();
            ((Cat)pet).removeTick();
        }
        else if( pet instanceof Dog )
        {
            pet.pet();
            ((Dog)pet).goForAWalk();
            ((Dog)pet).tellHimWhatHeIs();
        }
    }
}

The Caretaker does not know what kind of Pet he will get in advance and he my has several pets of different kinds. 看门人不知道他会提前获得哪种宠物,而我有几种不同类型的宠物。 I tried to give the Cute pet class a getType() method which returns a enum. 我试图给Cute宠物类一个getType()方法,该方法返回一个枚举。 With this enum i can remove the "instanceof" but the cast is still there. 使用这个枚举,我可以删除“ instanceof”,但是演员表仍然存在。

Am i missing something? 我想念什么吗?

If this were a real world problem, the caretaker would recognize which kind of pet he has based on the pet's appearance. 如果这是一个现实世界中的问题,看守者将根据宠物的外观识别出他拥有哪种宠物。 While "instance of" is one way of looking at it, you might want to consider overloading the takeCare method directly with the subtypes as required. 尽管“ instance of”是一种查看方式,但您可能需要考虑根据需要直接使用子类型重载takeCare方法。 For example: 例如:

class Caretaker {
    public void takeCare(Cat pet) {
        pet.pet();
        pet.letOutside();
        pet.letInside();
        pet.removeTick();
    }

    public void takeCare(Dog pet) {
        pet.pet();
        pet.goForAWalk();
        pet.tellHimWhatHeIs();
    }
}

in other words, the caretaker knows what to do (has methods already in place) for the kind of pet he receives. 换句话说,看管人知道该为他所接收的宠物做什么(已有方法)。

EDIT 编辑

In response to some of the comments, yes, the original example shifts the problem further up. 对于某些评论,是的,原始示例将问题进一步上移。 If you have an array or a list of generic pets then you still have to figure out what kinds of pets you have to give them to the caretaker. 如果您有一个阵列或一列普通宠物,那么您仍然必须弄清楚必须将哪种宠物交给看守。 Conceptually it seems strange that the pet should be able to pet itself, take itself for a walk, etc. (these methods are part of the pet class when it should be the caretaker doing these actions ON the pet). 从概念上讲,宠物应该能够自己养宠物,带自己去散步等(这是宠物类的一部分,当看护者应该对宠物执行这些操作时,这是宠物类的一部分),这似乎很奇怪。

I've since rewritten the code with a full working example below with a Job class that has a perform method. 从那以后,我用下面的完整工作示例重写了代码,其中包含具有perform方法的Job类。 This method will return the appropriate job based on the type of animal the caretaker has. 此方法将根据看护者所拥有的动物类型返回适当的工作。 The caretaker can then perform the job on the pet in question. 然后看守者可以对有关宠物执行工作。 See below. 见下文。

Doing things this way avoids instanceof . 以这种方式执行操作可以避免instanceof While it is debatable how good/bad instanceof actually is, where possible it should be the object itself to tell me what it needs, otherwise the whole polymorphism concept can get pretty hairy pretty quick. 尽管实例化的好坏有待商,,但应该尽可能让对象本身告诉我它的需求,否则整个多态概念很快就会变得很毛茸茸。

import java.util.Arrays;

public class Test {


    public static void main(String[] args) {
        Caretaker caretaker = new Caretaker();
        Arrays.asList(
                new Cat("Cat1"),
                new Cat("Cat2"),
                new Dog("Dog1")
        ).forEach(caretaker::takeCare);
    }

    interface CutePet {
        String whoAmI();
        Job whatINeed();
    }

    abstract static class NamedCutePet implements CutePet {
        private final String name;

        public NamedCutePet(String name) {
            this.name = name;
        }
        public String whoAmI() {
            return this.name;
        }
    }

    static class Cat extends NamedCutePet {

        public Cat(String name) {
            super(name);
        }

        @Override
        public Job whatINeed() {
            return new CatJob(this);
        }
    }

    static class Dog extends NamedCutePet {


        public Dog(String name) {
            super(name);
        }

        @Override
        public Job whatINeed() {
            return new DogJob(this);
        }
    }

    static class Caretaker {

        void takeCare(CutePet pet) {
            pet.whatINeed().perform();
        }
    }

    static abstract class BaseJob implements Job {

        void pet(CutePet pet) {
            System.out.println(String.format("The cute pet %s gets some pets", pet.whoAmI()));
        }
    }

    static class DogJob extends BaseJob {

        private final Dog dog;

        public DogJob(Dog dog) {
            this.dog = dog;
        }

        @Override
        public void perform() {
            pet(dog);
            takeDogFarAWalk(dog);
            tellHimWhatHeIs(dog);
        }
        private void takeDogFarAWalk(Dog dog) {
            System.out.println(String.format("The dog %s goes for a walk", dog.whoAmI()));
        }

        private void tellHimWhatHeIs(Dog dog) {
            System.out.println(String.format("The dog %s is a good boy", dog.whoAmI()));
        }
    }

    static class CatJob extends BaseJob {

        private final Cat cat;

        public CatJob(Cat cat) {
            this.cat = cat;
        }

        @Override
        public void perform() {
            pet(cat);
            letOutside(cat);
            letInside(cat);
            removeTick(cat);
        }

        private void letOutside(Cat cat) {
            System.out.println(String.format("The cat %s goes outside", cat.whoAmI()));
        }

        private void letInside(Cat cat) {
            System.out.println(String.format("The cat %s comes inside", cat.whoAmI()));
        }

        private void removeTick(Cat cat) {
            System.out.println(String.format("The cat %s loses all ticks", cat.whoAmI()));
        }
    }

    interface Job {
        void perform();
    }

}

Let's make it clear: you can't call subclass specific methods without typecasting to subclass type. 让我们说清楚:如果不将类型转换为子类类型,则无法调用特定于子类的方法。

Now, let me suggest an alternate way. 现在,让我提出另一种方法。 Define a method takeCare() in the superclass and let the subclasses implement it by calling several specific methods specific to subclasses. 在超类中定义方法takeCare() ,并让子类通过调用特定于子类的几种特定方法来实现它。 Then from CareTaker#takeCare() , call only takeCare() method without typecasting. 然后从CareTaker#takeCare() ,仅调用takeCare()方法,而无需进行类型转换。

Several other alternate approaches can be used to solve the situation. 可以使用其他几种替代方法来解决这种情况。

Here is how you would do it with interfaces and reflection. 这是使用接口和反射进行操作的方式。 Note that only the interface methods are called for each pet type. 请注意,每种宠物类型仅调用接口方法。 It could also be extended to call other methods. 它也可以扩展为调用其他方法。

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class PetProblem {

   public static void main(String[] args) {
      Caretaker caretaker = new Caretaker();
      Dog dog = new Dog();
      caretaker.takeCare(dog);
      System.out.println("\nNow do it for the cat\n");
      Cat cat = new Cat();
      caretaker.takeCare(cat);
   }

}

interface CuteCat {
   void letOutside();
   void letInside();
   void removeTick();
}

interface CuteDog {
   void goForAWalk();
   void tellHimWhatHeIs();
}

interface CutePet {
   default void pet() {
      System.out.println("The cute pet gets some pets");
   }
}

class Cat implements CutePet, CuteCat {
   public void letOutside() {
      System.out.println("The cat goes outside");
   }

   public void letInside() {
      System.out.println("The cat comes inside");
   }

   public void removeTick() {
      System.out.println("The cat looses all ticks");
   }
}

class Dog implements CutePet, CuteDog {
   public void goForAWalk() {
      System.out.println("The Dog goes for a walk");
   }

   public void tellHimWhatHeIs() {
      System.out.println("The Dog is a good boy");
   }
}

class Caretaker {
   public void takeCare(Object pet) {
      Class<?>[] ifss = pet.getClass().getInterfaces();
      for (Class<?> ifs : ifss) {
         Method[] methods = ifs.getDeclaredMethods();
         for (Method m : methods) {
            try {
               m.invoke(pet);
            }
            catch (IllegalAccessException | InvocationTargetException e) {
               e.printStackTrace();
            }
         }
      }
   }
}

Note however, that using interfaces and having a method so named that it can be used for all pets is easier. 但是请注意,使用接口并具有这样一种方法(该方法可以用于所有宠物)更容易。 Here is an example. 这是一个例子。 Since both dogs and cats need to eat, a common method feedMe() can be implemented for each. 由于猫和狗都需要吃饭,因此可以为每个对象实现通用方法feedMe()

public class AnimalShelter {
   public static void main(String[] args) {
      Caretaker caretaker = new Caretaker();
      Dog dog = new Dog();
      Cat cat = new Cat();
      caretaker.feedThePets(dog);
      caretaker.feedThePets(cat);
   }
}

interface SupperTime {
   void feedMe();
}

class Caretaker {
   public void feedThePets(SupperTime pet) {
      pet.feedMe();
   }
}

class Dog implements SupperTime {
   public void feedMe() {
      System.out.println("Oh boy, Kibbles n' Bits");
   }
}

class Cat implements SupperTime {
   public void feedMe() {
      System.out.println("Yum.  Purina Cat Chow");
   }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何根据 Spring MVC 控制器方法请求的 URI 为 @RequestBody 参数实例化特定的子类型? - How can I instantiate a specific sub-type for a @RequestBody parameter based on the requested URI for a Spring MVC controller method? 如何在子类型上使用为泛型超类型声明的Guava函数? - How to use Guava functions declared for a generic super-type on a sub-type? 为什么我不能使用引用子类型实例的父类型的引用来调用子类方法? - why can't I call a subclass method using a reference of a parent type that refers to an instance of a sub-type? 给定一个对象和一个类 <?> ,我可以判断该对象是否属于该类的子类型吗? (GWT,客户端) - Given an object and a Class<?>, can I tell whether the object is of sub-type of that class? (GWT, client-side) 有没有办法访问子类型方法而不使用泛型进行转换 - Is there any way to access a sub-type method without casting using generic 如果没有显式类型转换,语言是否可以“完整”? - Can a language be “complete” without explicit type casting? 将子类型转换为超类型 - Convert sub-type to super-type Hibernate Fetch不再是Join的子类型 - Hibernate Fetch is not longer sub-type of Join 尝试使用泛型对类进行子类型化 - Trying to sub-type classes using generics 如何获取 Office 文档的子类型 MIME,而不是在 Tika 中获取 OOXML - How to get Sub-type MIME of an Office document, instead of getting OOXML in Tika
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM