Bạn có thể viết các hàm / phương thức ảo trong Java không?


165

Có thể viết các phương thức ảo trong Java, như người ta sẽ làm trong C ++ không?

Hoặc, có một cách tiếp cận Java thích hợp mà bạn có thể thực hiện để tạo ra hành vi tương tự không? Tôi có thể vui lòng có một số ví dụ?

Câu trả lời:


305

Từ wikipedia

Trong Java , tất cả các phương thức không tĩnh theo mặc định là " các hàm ảo " . Chỉ các phương thức được đánh dấu bằng từ khóa cuối cùng , không thể bị ghi đè, cùng với các phương thức riêng , không được kế thừa, là không ảo .


3
Đây là một trong những câu trả lời của Jon Skeet .
Quazi Irfan

Tôi tự hỏi liệu điều đó có thực sự đúng không, bởi vì đối với những gì tôi đã đọc, trong Java, việc gửi phương thức động chỉ xảy ra với đối tượng mà phương thức được gọi trên - như đã giải thích ở đây vì vậy ví dụ giải thích các hàm ảo cho C ++ ở đây không hợp lệ cho java.
Bông cải xanh

@QuaziIrfan Đó là sự khác biệt giữa Java và C #.
Saletanth Karumanaghat

101

Bạn có thể viết các hàm ảo trong Java không?

Đúng. Trong thực tế, tất cả các phương thức cá thể trong Java là ảo theo mặc định. Chỉ một số phương thức nhất định không ảo:

  • Các phương thức lớp (vì thông thường mỗi cá thể chứa thông tin như một con trỏ tới vtable về các phương thức cụ thể của nó, nhưng không có trường hợp nào có sẵn ở đây).
  • Các phương thức cá thể riêng (vì không có lớp nào khác có thể truy cập vào phương thức, nên cá thể gọi luôn luôn là kiểu của lớp định nghĩa và do đó được biết rõ ràng vào thời gian biên dịch).

Dưới đây là một số ví dụ:

Các chức năng ảo "bình thường"

Ví dụ sau đây là từ một phiên bản cũ của trang wikipedia được đề cập trong câu trả lời khác.

import java.util.*;

public class Animal 
{
   public void eat() 
   { 
      System.out.println("I eat like a generic Animal."); 
   }

   public static void main(String[] args) 
   {
      List<Animal> animals = new LinkedList<Animal>();

      animals.add(new Animal());
      animals.add(new Fish());
      animals.add(new Goldfish());
      animals.add(new OtherAnimal());

      for (Animal currentAnimal : animals) 
      {
         currentAnimal.eat();
      }
   }
}

class Fish extends Animal 
{
   @Override
   public void eat() 
   { 
      System.out.println("I eat like a fish!"); 
   }
}

class Goldfish extends Fish 
{
   @Override
   public void eat() 
   { 
      System.out.println("I eat like a goldfish!"); 
   }
}

class OtherAnimal extends Animal {}

Đầu ra:

Tôi ăn như một con thú chung chung.
Tôi ăn như một con cá!
Tôi ăn như một con cá vàng!
Tôi ăn như một con thú chung chung.

Ví dụ với các hàm ảo có giao diện

Các phương thức giao diện Java đều là ảo. Chúng phải là ảo vì chúng dựa vào các lớp triển khai để cung cấp các cài đặt phương thức. Mã để thực thi sẽ chỉ được chọn trong thời gian chạy.

Ví dụ:

interface Bicycle {         //the function applyBrakes() is virtual because
    void applyBrakes();     //functions in interfaces are designed to be 
}                           //overridden.

class ACMEBicycle implements Bicycle {
    public void applyBrakes(){               //Here we implement applyBrakes()
       System.out.println("Brakes applied"); //function
    }
}

Ví dụ với các hàm ảo với các lớp trừu tượng.

Tương tự như giao diện Các lớp trừu tượng phải chứa các phương thức ảo vì chúng dựa vào việc thực hiện các lớp mở rộng. Ví dụ:

abstract class Dog {                   
    final void bark() {               //bark() is not virtual because it is 
        System.out.println("woof");   //final and if you tried to override it
    }                                 //you would get a compile time error.

    abstract void jump();             //jump() is a "pure" virtual function 
}                                     
class MyDog extends Dog{
    void jump(){
        System.out.println("boing");    //here jump() is being overridden
    }                                  
}
public class Runner {
    public static void main(String[] args) {
        Dog dog = new MyDog();       // Create a MyDog and assign to plain Dog variable
        dog.jump();                  // calling the virtual function.
                                     // MyDog.jump() will be executed 
                                     // although the variable is just a plain Dog.
    }
}

1
Đây phải là câu trả lời đầy đủ nhất. Nó cung cấp 2 cách để thực hiện một chức năng ảo vì java không có từ khóa. Cảm ơn bạn.
Christopher Bales

Câu trả lời tốt hơn nhiều so với trích dẫn Wikipedia. Đến từ c ++ và lười biếng với các nghiên cứu Java của tôi, trừu tượng là thứ tôi đang tìm kiếm.
David

@David Câu trả lời này tốt hơn như thế nào? Các trích dẫn wikipedia là đầy đủ, súc tích và chính xác. Câu trả lời này, ngược lại, không đề cập đến con voi trong phòng: Theo mặc định, tất cả các chức năng trong Java (với các ngoại lệ được liệt kê trong bài viết trên wikipedia) là ảo. Cả các lớp trừu tượng và giao diện đều không cần thiết cho các hàm ảo, vì vậy điều đó chỉ thêm nhiễu gây hiểu lầm. Và sau đó, "đòi hỏi kỹ năng giao tiếp tuyệt vời và nắm vững sâu sắc các nguyên tắc cơ bản" ... jeez. Đó là một tuyên bố tự làm sai ngay tại đó: Không ai có thể lãng phí không gian đĩa có giá trị với nó.
Peter - Tái lập Monica

Bài đăng trên wikipedia kém hơn và ít cụ thể hơn cho câu trả lời này vì nó là về khái niệm các chức năng ảo trong bất kỳ ngôn ngữ nào, thay vì chỉ java. Ví dụ được đưa ra trong trang wikipedia được viết bằng C, và nó không hoàn chỉnh nhất, và nó dễ gây hiểu lầm hơn. Chi tiết về tất cả các chức năng là ảo và bạn không cần các lớp hoặc giao diện trừu tượng để có các chức năng ảo bị nhiễu. Tôi không bao giờ nói họ được yêu cầu, bạn đọc sai đó. Tôi không hiểu điểm cuối cùng của bạn, bạn có muốn tôi xóa câu hỏi này vì bạn không thích nó?
Eric Leschinski

1
Vài năm muộn ở đây nhưng câu trả lời tuyệt vời
Tom O.

55

Tất cả các hàm trong Java là ảo theo mặc định.

Bạn phải tìm cách viết các hàm không ảo bằng cách thêm từ khóa "cuối cùng".

Điều này trái ngược với mặc định của C ++ / C #. Các hàm lớp không ảo theo mặc định; bạn làm cho chúng như vậy bằng cách thêm công cụ sửa đổi "ảo".


4
Các chức năng riêng tư như đã nêu trong câu trả lời của Klaus cũng không phải là ảo.
Don Larynx

9

Tất cả các phương thức cá thể không riêng tư là ảo theo mặc định trong Java.

Trong C ++, các phương thức riêng tư có thể là ảo. Điều này có thể được khai thác cho thành ngữ không giao diện ảo (NVI). Trong Java, bạn cần làm cho các phương thức có thể ghi đè NVI được bảo vệ.

Từ Đặc tả ngôn ngữ Java, v3:

8.4.8.1 Ghi đè (bằng phương thức sơ thẩm) Phương thức cá thể m1 được khai báo trong lớp C ghi đè phương thức cá thể khác, m2, được khai báo trong lớp A iff tất cả những điều sau đây là đúng:

  1. C là một lớp con của A.
  2. Chữ ký của m1 là một phần phụ (§8.4.2) của chữ ký m2.
  3. * M2 là công khai, được bảo vệ hoặc khai báo với quyền truy cập mặc định trong cùng gói với C hoặc * m1 ghi đè phương thức m3, m3 khác với m1, m3 khác với m2, sao cho m3 ghi đè lên m2.


1

Trong Java, tất cả các biến & hàm công khai (không riêng tư) là Ảo theo mặc định. Hơn nữa các biến & chức năng sử dụng từ khóa cuối cùng không phải là ảo .


"Biến ảo" nghĩa là gì?
neoexpert
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.