Sự khác biệt giữa đúc lên và đúc xuống đối với biến lớp


138

Sự khác biệt giữa đúc lên và đúc xuống đối với biến lớp là gì?

Ví dụ: trong lớp chương trình sau đây Animal chỉ chứa một phương thức nhưng lớp Dog chứa hai phương thức, sau đó chúng ta chuyển biến Dog thành Biến động vật như thế nào.

Nếu quá trình đúc được thực hiện thì làm sao chúng ta có thể gọi phương thức khác của Dog bằng biến Animal.

class Animal 
{ 
    public void callme()
    {
        System.out.println("In callme of Animal");
    }
}


class Dog extends Animal 
{ 
    public void callme()
    {
        System.out.println("In callme of Dog");
    }

    public void callme2()
    {
        System.out.println("In callme2 of Dog");
    }
}

public class UseAnimlas 
{
    public static void main (String [] args) 
    {
        Dog d = new Dog();      
        Animal a = (Animal)d;
        d.callme();
        a.callme();
        ((Dog) a).callme2();
    }
}

A Doglà một Animal. Hầu hết thời gian u ám là không cần thiết trừ khi bạn muốn sử dụng một phương thức quá tải nhất định. callmetồn tại trong cả hai AnimalDog. callme2chỉ tồn tại trong Dog, mà bạn đúc ađể Doglàm cho nó làm việc.
Brian

Đầu ra của mã của bạn là gì?
Malwinder Singh

Điều thú vị là, d.callme trả lại 'In callme of Dog' mặc dù d đã được đúc thành động vật !!
Chris311

4
@ Chris311 cả 'd' và 'a' đều trỏ đến cùng một đối tượng ... đó là Dog, nhưng 'a' chỉ có quyền truy cập vào các phương thức cụ thể của Dog khi nó bị downcast khi chạy. Nguyên vẹn: Động vật a = (Động vật) d; là không cần thiết, bạn chỉ cần Animal a = d; như bạn đang u ám.
Đánh dấu

Câu trả lời:


223

Upcasting đang truyền tới một siêu kiểu, trong khi downcasting đang truyền tới một kiểu con. Upcasting luôn được cho phép, nhưng downcasting liên quan đến kiểm tra loại và có thể ném a ClassCastException.

Trong trường hợp của bạn, một diễn viên từ một Dogđến một Animallà một u ám, bởi vì một Dog-a Animal. Nói chung, bạn có thể u ám bất cứ khi nào có mối quan hệ giữa hai lớp.

Downcasting sẽ là một cái gì đó như thế này:

Animal animal = new Dog();
Dog castedDog = (Dog) animal;

Về cơ bản những gì bạn đang làm là nói với trình biên dịch rằng bạn biết loại thời gian chạy của đối tượng thực sự là gì. Trình biên dịch sẽ cho phép chuyển đổi, nhưng vẫn sẽ chèn kiểm tra độ sạch thời gian chạy để đảm bảo rằng chuyển đổi có ý nghĩa. Trong trường hợp này, việc truyền là có thể bởi vì trong thời gian chạy animalthực sự là một kiểu Dogmặc dù kiểu tĩnh animalAnimal.

Tuy nhiên, nếu bạn định làm điều này:

Animal animal = new Animal();
Dog notADog = (Dog) animal;

Bạn sẽ nhận được một ClassCastException. Lý do là bởi vì animalkiểu thời gian chạy là Animal, và vì vậy khi bạn nói với bộ thực thi để thực hiện các vai diễn, nó thấy đó animalkhông thực sự là một Dogvà vì vậy ném a ClassCastException.

Để gọi phương thức của siêu lớp, bạn có thể thực hiện super.method()hoặc bằng cách thực hiện việc phát sóng.

Để gọi phương thức của lớp con, bạn phải thực hiện downcast. Như được hiển thị ở trên, bạn thường có nguy cơ a ClassCastExceptionbằng cách làm điều này; tuy nhiên, bạn có thể sử dụng instanceoftoán tử để kiểm tra loại thời gian chạy của đối tượng trước khi thực hiện truyền, cho phép bạn ngăn ClassCastExceptions:

Animal animal = getAnimal(); // Maybe a Dog? Maybe a Cat? Maybe an Animal?
if (animal instanceof Dog) {
    // Guaranteed to succeed, barring classloader shenanigans
    Dog castedDog = (Dog) animal;
}

Liệu downcasting thích hợp đảm bảo không ClassCastExceptionhay không? Giống như trong trường hợp đầu tiên?
Malwinder Singh

@MS Ý của bạn là "thích hợp" là gì?
awksp

2
@awksp Đây là một câu trả lời xuất sắc và rõ ràng. Khá nhiều tổng hợp tất cả mọi thứ tôi cần biết về Casting.
Gautham Honnavara

chắc chắn bạn chưa từng tham gia lớp học mà động vật là một ví dụ của con chó phải không? vậy tại sao bạn thậm chí kiểm tra nó?
barlop

62

Đúc xuống và đúc lên như sau:
nhập mô tả hình ảnh ở đây

Upcasting : Khi chúng tôi muốn chuyển một lớp Sub thành Super class, chúng tôi sử dụng Upcasting (hoặc mở rộng). Nó xảy ra tự động, không cần phải làm gì rõ ràng.

Downcasting : Khi chúng ta muốn truyền một Super class thành Sub class, chúng ta sử dụng Downcasting (hoặc thu hẹp) và Downcasting không thể trực tiếp trong Java, rõ ràng chúng ta phải làm.

Dog d = new Dog();
Animal a = (Animal) d; //Explicitly you have done upcasting. Actually no need, we can directly type cast like Animal a = d; compiler now treat Dog as Animal but still it is Dog even after upcasting
d.callme();
a.callme(); // It calls Dog's method even though we use Animal reference.
((Dog) a).callme2(); // Downcasting: Compiler does know Animal it is, In order to use Dog methods, we have to do typecast explicitly.
// Internally if it is not a Dog object it throws ClassCastException

Vì vậy, không có cách nào để làm ầm ĩ để gọi cha mẹ của phương thức?
karlihnos

32

Upcasting và downcasting là một phần quan trọng của Java, cho phép chúng ta xây dựng các chương trình phức tạp bằng cú pháp đơn giản và mang lại cho chúng ta những lợi thế lớn, như Đa hình hoặc nhóm các đối tượng khác nhau. Java cho phép một đối tượng của một loại lớp con được coi là một đối tượng của bất kỳ loại siêu lớp nào. Điều này được gọi là u ám. Upcasting được thực hiện tự động, trong khi downcasting phải được lập trình viên thực hiện thủ công và tôi sẽ cố gắng hết sức để giải thích tại sao lại như vậy.

Upcasting và downcasting KHÔNG giống như truyền nguyên thủy từ cái này sang cái khác, và tôi tin rằng đó là điều gây ra nhiều nhầm lẫn, khi lập trình viên bắt đầu học đúc các đối tượng.

Đa hình: Tất cả các phương thức trong java là ảo theo mặc định. Điều đó có nghĩa là bất kỳ phương thức nào cũng có thể bị ghi đè khi được sử dụng trong kế thừa, trừ khi phương thức đó được khai báo là cuối cùng hoặc tĩnh .

Bạn có thể xem ví dụ bên dưới cách getType();hoạt động theo loại đối tượng (Dog, Pet, Police Dog).

Giả sử bạn có ba con chó

  1. Chó - Đây là siêu hạng.

  2. Pet Dog - Pet Dog kéo dài Dog.

  3. Chó cảnh sát - Chó cảnh sát kéo dài Chó cưng.

    public class Dog{ 
       public String getType () {
          System.out.println("NormalDog");
          return "NormalDog";
       }
     }
    
    /**
     * Pet Dog has an extra method dogName()
     */   
    public class PetDog extends Dog{ 
       public String getType () {
          System.out.println("PetDog");
          return "PetDog";
       }
       public String dogName () {
          System.out.println("I don't have Name !!");
          return "NO Name";
       }
     }
    
    /**
     * Police Dog has an extra method secretId()
     */
    public class PoliceDog extends PetDog{
    
     public String secretId() {
        System.out.println("ID");
        return "ID";
     }
    
     public String getType () {
         System.out.println("I am a Police Dog");
         return "Police Dog";
     }
    }

Đa hình: Tất cả các phương thức trong java là ảo theo mặc định. Điều đó có nghĩa là bất kỳ phương thức nào cũng có thể bị ghi đè khi được sử dụng trong kế thừa, trừ khi phương thức đó được khai báo là cuối cùng hoặc tĩnh. (Giải thích thuộc về khái niệm bảng ảo)

Bảng ảo / Bảng điều phối: Bảng điều phối của đối tượng sẽ chứa các địa chỉ của các phương thức ràng buộc động của đối tượng. Các cuộc gọi phương thức được thực hiện bằng cách tìm nạp địa chỉ của phương thức từ bảng điều phối của đối tượng. Bảng điều phối giống nhau cho tất cả các đối tượng thuộc cùng một lớp và do đó thường được chia sẻ giữa chúng.

public static void main (String[] args) {
      /**
       * Creating the different objects with super class Reference
       */
     Dog obj1 = new Dog();
`         /**
           *  Object of Pet Dog is created with Dog Reference since                
           *  Upcasting is done automatically for us we don't have to worry about it 
           *  
           */
     Dog obj2 = new PetDog();
`         /**
           *  Object of Police Dog is created with Dog Reference since                
           *  Upcasting is done automatically for us we don't have to worry       
           *  about it here even though we are extending PoliceDog with PetDog 
           *  since PetDog is extending Dog Java automatically upcast for us 
           */
      Dog obj3 = new PoliceDog();
}



 obj1.getType();

Bản in Normal Dog

  obj2.getType();

Bản in Pet Dog

 obj3.getType();

Bản in Police Dog

Downcasting cần phải được thực hiện bởi các lập trình viên

Khi bạn cố gắng để gọi secretID();phương pháp trên obj3được PoliceDog objectnhưng tham chiếu đến Dogmà là một lớp siêu trong hệ thống phân cấp nó throws lỗi vì obj3không được tiếp cận với secretId()phương pháp. Để gọi phương thức đó, bạn cần Downcast mà obj3 thủ công để PoliceDog

  ( (PoliceDog)obj3).secretID();

mà in ID

Theo cách tương tự để gọi dogName();phương pháp trong PetDoglớp học mà bạn cần phải nhìn xuống obj2để PetDogtừ obj2 được tham chiếu tới Dogvà không được tiếp cận với dogName();phương pháp

  ( (PetDog)obj2).dogName();

Tại sao lại như vậy, việc upcasting là tự động, nhưng downcasting phải là thủ công? Vâng, bạn thấy đấy, u ám không bao giờ có thể thất bại. Nhưng nếu bạn có một nhóm các Chó khác nhau và muốn downCast tất cả chúng vào một chủng loại, sau đó có một cơ hội, mà một số trong những Chó thực sự của các loại khác nhau tức là, PetDog, PoliceDog, và quá trình thất bại, bởi ném ClassCastException.

Đây là lý do bạn cần hạ thấp các đối tượng của mình theo cách thủ công nếu bạn đã tham chiếu các đối tượng của mình với loại siêu hạng.

Lưu ý: Ở đây bằng cách tham chiếu có nghĩa là bạn không thay đổi địa chỉ bộ nhớ của các từ chối của bạn khi bạn hạ thấp nó, nó vẫn giống như bạn chỉ nhóm chúng thành loại cụ thể trong trường hợp này Dog


'Đa hình sử dụng tự động downcast trong khi gọi phương thức.' Không, nó không có. Cơ chế được sử dụng không được chỉ định, nhưng cơ chế thông thường nhất - một vtable - không có điều đó. Nhìn vào mã đối tượng. Không u ám.
Hầu tước Lorne

Tại sao không? Đây là những gì xảy ra đúng, bạn có thể đưa ra một ví dụ khi nó không hoạt động?
Nagarjuna Yelisetty

1
Tại sao không? Đây là những gì xảy ra đúng .. bạn có thể đưa ra một ví dụ trong đó câu lệnh 'Đa hình sử dụng chế độ downcast tự động trong khi gọi phương thức'. sẽ thất bại hay sẽ không đúng
Nagarjuna Yelisetty

Đó là sự ganh đua của bạn. Tùy bạn chứng minh điều đó. Hiển thị nơi trong mã đối tượng xảy ra downcast. Câu trả lời cho câu hỏi "tại sao không" là "bởi vì nó không cần thiết". Vtable đảm nhiệm việc gửi phương thức và biến đã trỏ đến toàn bộ đối tượng.
Hầu tước Lorne

1
'Theo hiểu biết của tôi, tuyên bố của tôi là đúng và nó đúng trong mọi trường hợp' không phải là một bằng chứng. Đó là một khẳng định đơn thuần. Tôi đang yêu cầu bạn chứng minh tuyên bố của bạn . Bạn không làm như vậy. Trong thực tế, bạn chỉ lặp lại chính mình. Và tôi đã cung cấp một số lời bác bỏ. Tôi cũng đã cung cấp một thủ tục quyết định. Nếu bạn có thể tìm thấy một downcast trong mã đối tượng cho một cuộc gọi phương thức, bạn đã đúng và tôi sai. Đây là cách khoa học được thực hiện. Làm đi. Và tuyên bố rằng tôi 'mạnh dạn phụ thuộc vào tài liệu' là một sự xuyên tạc trắng trợn. Đừng làm vậy.
Hầu tước Lorne

12

Tôi biết câu hỏi này đã được hỏi cách đây khá lâu nhưng đối với những người dùng mới của câu hỏi này. Vui lòng đọc bài viết này trong đó có mô tả đầy đủ về upcasting, downcasting và sử dụng toán tử instanceof

  • Không cần phải upcast thủ công, nó tự xảy ra:

    Mammal m = (Mammal)new Cat(); tương đương với Mammal m = new Cat();

  • Nhưng downcasting phải luôn luôn được thực hiện thủ công:

    Cat c1 = new Cat();      
    Animal a = c1;      //automatic upcasting to Animal
    Cat c2 = (Cat) a;    //manual downcasting back to a Cat

Tại sao lại như vậy, việc upcasting là tự động, nhưng downcasting phải là thủ công? Vâng, bạn thấy đấy, u ám không bao giờ có thể thất bại. Nhưng nếu bạn có một nhóm Động vật khác nhau và muốn hạ bệ tất cả chúng thành Mèo, thì có khả năng, một số Động vật này thực sự là Chó và quá trình thất bại, bằng cách ném ClassCastException. Đây là nơi sẽ giới thiệu một tính năng hữu ích gọi là "instanceof" , để kiểm tra xem một đối tượng có phải là một thể hiện của Class không.

 Cat c1 = new Cat();         
    Animal a = c1;       //upcasting to Animal
    if(a instanceof Cat){ // testing if the Animal is a Cat
        System.out.println("It's a Cat! Now i can safely downcast it to a Cat, without a fear of failure.");        
        Cat c2 = (Cat)a;
    }

Để biết thêm thông tin xin vui lòng đọc bài viết này


điểm tốt: Động vật có vú m = (Động vật có vú) Mèo mới (); bằng với Động vật có vú m = new Cat ();
Catbuilts

6

Tốt hơn nên thử phương pháp này để phát sóng, thật dễ hiểu:

/* upcasting problem */
class Animal
{ 
    public void callme()
    {
        System.out.println("In callme of Animal");
    }
}

class Dog extends Animal 
{ 
    public void callme()
    {
        System.out.println("In callme of Dog");
    }

    public void callme2()
    {
        System.out.println("In callme2 of Dog");
    }
}

public class Useanimlas 
{
    public static void main (String [] args) 
    {
        Animal animal = new Animal ();
        Dog dog = new Dog();
        Animal ref;
        ref = animal;
        ref.callme();
        ref = dog;
        ref.callme();
    }
}

và trong dòng cuối cùng có thể là: ((Dog) ref) .callme2 (); // để truy cập phương thức downcasting / hẹp và callme2 () của lớp Dog.
udarH3

6

Có lẽ bảng này giúp. Gọi callme()phương thức của lớp Parenthoặc lớp Child. Theo nguyên tắc:

NÂNG CẤP -> Ẩn

TẢI XUỐNG -> Tiết lộ

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây


4

1.- u ám.

Thực hiện một chương trình phát sóng, bạn xác định một loại thẻ nào đó, trỏ đến một đối tượng của một kiểu con (Loại và kiểu con có thể được gọi là lớp và lớp con, nếu bạn cảm thấy thoải mái hơn ...).

Animal animalCat = new Cat();

Điều đó có nghĩa là thẻ đó, AnimalCat, sẽ chỉ có chức năng (các phương thức) của loại Động vật, bởi vì chúng tôi đã tuyên bố nó là loại Động vật, không phải là loại Mèo.

Chúng tôi được phép làm điều đó theo cách "tự nhiên / ẩn / tự động", tại thời gian biên dịch hoặc tại thời gian chạy, chủ yếu là do Cat thừa hưởng một số chức năng của nó từ Animal; ví dụ: di chuyển (). (Ít nhất, mèo là một con vật, phải không?)

2.- Downcasting.

Nhưng, điều gì sẽ xảy ra nếu chúng ta cần có chức năng của Cat, từ thẻ Animal loại của chúng tôi?.

Khi chúng ta đã tạo thẻ AnimalCat trỏ đến một đối tượng Cat, chúng ta cần một cách để gọi các phương thức đối tượng Cat, từ thẻ AnimalCat của chúng ta theo một cách khá thông minh.

Thủ tục như vậy là những gì chúng ta gọi là Downcasting, và chúng ta chỉ có thể làm điều đó vào thời gian chạy.

Thời gian cho một số mã:

public class Animal {
    public String move() {
        return "Going to somewhere";
    }
}

public class Cat extends Animal{
    public String makeNoise() {
        return "Meow!";
    }   
}

public class Test {

    public static void main(String[] args) {
        
    //1.- Upcasting 
    //  __Type_____tag________object
        Animal animalCat = new Cat();
    //Some animal movement
        System.out.println(animalCat.move());
        //prints "Going to somewhere"
        
    //2.- Downcasting   
    //Now you wanna make some Animal noise.
        //First of all: type Animal hasn't any makeNoise() functionality.
        //But Cat can do it!. I wanna be an Animal Cat now!!
        
        //___________________Downcast__tag_____ Cat's method
        String animalNoise = ( (Cat) animalCat ).makeNoise();
        
        System.out.println(animalNoise);
        //Prints "Meow!", as cats usually done.
        
    //3.- An Animal may be a Cat, but a Dog or a Rhinoceros too.
        //All of them have their own noises and own functionalities.
        //Uncomment below and read the error in the console:
        
    //  __Type_____tag________object
        //Cat catAnimal = new Animal();
        
    }

}

2

Phụ huynh: Xe
con: Figo
Xe c1 = new Figo ();

=====
Upcasting: -
Phương thức: Đối tượng c1 sẽ đề cập đến Phương thức của lớp (Figo - Phương thức phải được ghi đè) vì lớp "Figo" được chỉ định bằng "mới".
Biến thể lập thể: Đối tượng c1 sẽ đề cập đến biến thể hiện của Lớp khai báo ("Xe hơi").

Khi lớp Khai báo là cha mẹ và đối tượng được tạo ra của con thì việc truyền ẩn sẽ xảy ra đó là "Upcasting".

======
Downcasting: -
Figo f1 = (Figo) c1; //
Phương thức: Đối tượng F1 sẽ đề cập đến Phương thức lớp (figo) khi đối tượng ban đầu c1 được tạo với lớp "Figo". nhưng một khi quá trình truyền xuống được thực hiện, các phương thức chỉ xuất hiện trong lớp "Figo" cũng có thể được gọi bằng biến F1.
Biến thể lập thể: Đối tượng F1 sẽ không tham chiếu biến thể hiện của lớp Khai báo của đối tượng c1 (lớp khai báo cho c1 là CAR) nhưng với việc truyền xuống, nó sẽ tham chiếu các biến thể hiện của lớp Figo.

======
Sử dụng: Khi Đối tượng thuộc Lớp con và lớp khai báo là Lớp cha và lớp con muốn truy cập biến Instance của lớp riêng và không phải lớp cha thì có thể thực hiện bằng "Downcasting".


1

upcasting có nghĩa là truyền đối tượng thành một siêu kiểu, trong khi downcasting có nghĩa là truyền tới một kiểu con.

Trong java, upcasting là không cần thiết vì nó được thực hiện tự động. Và nó thường được gọi là đúc ngầm. Bạn có thể chỉ định nó để làm cho nó rõ ràng cho người khác.

Như vậy, viết

Animal a = (Animal)d;

hoặc là

Animal a = d;

dẫn đến chính xác cùng một điểm và trong cả hai trường hợp sẽ được thực hiện callme()từ Dog.

Thay vào đó, việc truyền phát là cần thiết vì bạn đã xác định alà đối tượng của Động vật. Hiện tại bạn biết đó là một Dog, nhưng java không đảm bảo điều đó. Trên thực tế tại thời gian chạy nó có thể khác và java sẽ ném ClassCastException, điều đó sẽ xảy ra. Tất nhiên đó không phải là trường hợp mẫu của bạn. Nếu bạn không đúc ađến Animal, java thậm chí không thể biên dịch ứng dụng vì Animalkhông có phương pháp callme2().

Trong ví dụ của bạn, bạn không thể đạt được các mã của callme()các Animaltừ UseAnimlas(vì Dogghi đè nó) trừ khi phương pháp này sẽ là như sau:

class Dog extends Animal 
{ 
    public void callme()
    {
        super.callme();
        System.out.println("In callme of Dog");
    }
    ... 
} 

0

Chúng ta có thể tạo đối tượng để Downcasting. Trong loại này cũng có. : gọi các phương thức lớp cơ sở

Animal a=new Dog();
a.callme();
((Dog)a).callme2();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.