簡體   English   中英

如果要使用子類型的屬性,如何防止顯式轉換?

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

因此,編譯器會在我進行顯式轉換時抱怨。 我可以通過使用@SuppressWarnings注釋來防止這種情況。 在這一點上,我的代碼中會有很多注釋,這讓我懷疑還有另一種我不知道的方法。

讓我們看看這個例子

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();
        }
    }
}

看門人不知道他會提前獲得哪種寵物,而我有幾種不同類型的寵物。 我試圖給Cute寵物類一個getType()方法,該方法返回一個枚舉。 使用這個枚舉,我可以刪除“ instanceof”,但是演員表仍然存在。

我想念什么嗎?

如果這是一個現實世界中的問題,看守者將根據寵物的外觀識別出他擁有哪種寵物。 盡管“ instance of”是一種查看方式,但您可能需要考慮根據需要直接使用子類型重載takeCare方法。 例如:

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();
    }
}

換句話說,看管人知道該為他所接收的寵物做什么(已有方法)。

編輯

對於某些評論,是的,原始示例將問題進一步上移。 如果您有一個陣列或一列普通寵物,那么您仍然必須弄清楚必須將哪種寵物交給看守。 從概念上講,寵物應該能夠自己養寵物,帶自己去散步等(這是寵物類的一部分,當看護者應該對寵物執行這些操作時,這是寵物類的一部分),這似乎很奇怪。

從那以后,我用下面的完整工作示例重寫了代碼,其中包含具有perform方法的Job類。 此方法將根據看護者所擁有的動物類型返回適當的工作。 然后看守者可以對有關寵物執行工作。 見下文。

以這種方式執行操作可以避免instanceof 盡管實例化的好壞有待商,,但應該盡可能讓對象本身告訴我它的需求,否則整個多態概念很快就會變得很毛茸茸。

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();
    }

}

讓我們說清楚:如果不將類型轉換為子類類型,則無法調用特定於子類的方法。

現在,讓我提出另一種方法。 在超類中定義方法takeCare() ,並讓子類通過調用特定於子類的幾種特定方法來實現它。 然后從CareTaker#takeCare() ,僅調用takeCare()方法,而無需進行類型轉換。

可以使用其他幾種替代方法來解決這種情況。

這是使用接口和反射進行操作的方式。 請注意,每種寵物類型僅調用接口方法。 它也可以擴展為調用其他方法。

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();
            }
         }
      }
   }
}

但是請注意,使用接口並具有這樣一種方法(該方法可以用於所有寵物)更容易。 這是一個例子。 由於貓和狗都需要吃飯,因此可以為每個對象實現通用方法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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM