Czy potrafisz pisać funkcje / metody wirtualne w Javie?

165

Czy można pisać metody wirtualne w Javie, tak jak w C ++?

A może istnieje odpowiednie podejście do języka Java, które można zaimplementować i które daje podobne zachowanie? Czy mógłbym prosić o kilka przykładów?

yonatan
źródło

Odpowiedzi:

305

Z Wikipedii

W Javie wszystkie metody niestatyczne są domyślnie „ funkcjami wirtualnymi. Tylko metody oznaczone słowem kluczowym final , których nie można przesłonić, oraz metody prywatne , które nie są dziedziczone, są niewirtualne .

Klaus Byskov Pedersen
źródło
3
Oto jedna z odpowiedzi Jona Skeeta .
Quazi Irfan
Zastanawiałem się, czy to prawda, ponieważ dla tego, co przeczytałem, w Javie dynamiczne wysyłanie metod odbywa się tylko dla obiektu, na którym metoda jest wywoływana - jak wyjaśniono tutaj, więc przykład wyjaśniający funkcje wirtualne dla C ++ tutaj nie jest ważny dla Java.
Brokuły
@QuaziIrfan Na tym polega różnica między Javą a C #.
Sreekanth Karumanaghat
101

Czy potrafisz pisać funkcje wirtualne w Javie?

Tak. W rzeczywistości wszystkie metody instancji w Javie są domyślnie wirtualne. Tylko niektóre metody nie są wirtualne:

  • Metody klas (ponieważ zazwyczaj każda instancja zawiera informacje, takie jak wskaźnik do tabeli vtable, dotyczące jej określonych metod, ale żadna instancja nie jest tutaj dostępna).
  • Metody instancji prywatnych (ponieważ żadna inna klasa nie ma dostępu do metody, instancja wywołująca ma zawsze typ samej klasy definiującej i dlatego jest jednoznacznie znana w czasie kompilacji).

Oto kilka przykładów:

„Normalne” funkcje wirtualne

Poniższy przykład pochodzi ze starej wersji strony wikipedii, o której mowa w innej odpowiedzi.

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 {}

Wynik:

Jem jak zwykłe zwierzę.
Jem jak ryba!
Jem jak złota rybka!
Jem jak zwykłe zwierzę.

Przykład z funkcjami wirtualnymi z interfejsami

Wszystkie metody interfejsu Java są wirtualne. Oni muszą być wirtualny, ponieważ opierają się one na zajęciach wykonawczych w celu zapewnienia implementacje metod. Kod do wykonania zostanie wybrany tylko w czasie wykonywania.

Na przykład:

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
    }
}

Przykład z funkcjami wirtualnymi z klasami abstrakcyjnymi.

Podobnie jak w przypadku interfejsów Klasy abstrakcyjne muszą zawierać metody wirtualne, ponieważ opierają się na implementacji klas rozszerzających. Na przykład:

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.
    }
}
Eric Leschinski
źródło
1
To musi być najpełniejsza odpowiedź. Zapewnia 2 sposoby implementacji funkcji wirtualnej, ponieważ java nie ma słowa kluczowego. Dziękuję Ci.
Christopher Bales
O wiele lepsza odpowiedź niż cytat z Wikipedii. Wychodząc z języka C ++ i leniwym podczas nauki języka Java, szukałem abstrakcji.
David
@David W jaki sposób ta odpowiedź jest lepsza? Cytat z Wikipedii jest kompletny, zwięzły i poprawny. W tej odpowiedzi nie wspomina się natomiast o słoniu w pokoju: Domyślnie wszystkie funkcje w Javie (z wyjątkami wymienionymi w artykule na Wikipedii) są wirtualne. Ani abstrakcyjne klasy, ani interfejsy nie są konieczne dla funkcji wirtualnych, więc dodaje to tylko wprowadzającego w błąd szumu. A potem to „wymaga wielkich umiejętności komunikacyjnych i głębokiego opanowania podstawowych zasad”… Jezu. To jest samo-fałszujące się stwierdzenie: nikt, kto to miał, nie marnowałby cennego miejsca na dysku.
Peter - Przywróć Monikę
Post na Wikipedii jest gorszy i mniej specyficzny dla tej odpowiedzi, ponieważ dotyczy koncepcji funkcji wirtualnych w dowolnym języku, a nie tylko w Javie. Przykład podany na stronie wikipedii jest napisany w języku C i jest w najlepszym przypadku niepełny i bardziej mylący. Szczegółowy opis wszystkich funkcji, które są wirtualne i że nie są potrzebne abstrakcyjne klasy lub interfejsy, aby funkcje wirtualne były zakłócane. Nigdy nie powiedziałem, że są wymagane, źle to odczytałeś. Nie rozumiem twojego ostatniego punktu, czy chcesz, żebym usunął to pytanie, ponieważ ci się nie podoba?
Eric Leschinski
1
Kilka lat później, ale fantastyczna odpowiedź
Tom O.
55

Wszystkie funkcje w Javie są domyślnie wirtualne.

Aby napisać funkcje niewirtualne, musisz zrobić wszystko, co w twojej mocy, dodając słowo kluczowe „final”.

Jest to przeciwieństwo domyślnego C ++ / C #. Funkcje klas są domyślnie niewirtualne; robisz je tak, dodając modyfikator „wirtualny”.

duffymo
źródło
4
funkcje prywatne, jak stwierdzono w odpowiedzi Klausa, również nie są wirtualne.
Don Larynx
9

Wszystkie non-prywatne metody instancji są wirtualne domyślnie w Javie.

W C ++ metody prywatne mogą być wirtualne. Można to wykorzystać w przypadku idiomu interfejsu niewirtualnego (NVI). W Javie należałoby zabezpieczyć metody zastępowania NVI.

Ze specyfikacji języka Java, wersja 3:

8.4.8.1 Przesłanianie (przez metody instancji) Metoda instancji m1 zadeklarowana w klasie C przesłania inną metodę instancji m2, zadeklarowaną w klasie A iff, wszystkie poniższe są prawdziwe:

  1. C jest podklasą A.
  2. Podpis m1 jest podpisem (§ 8.4.2) podpisu m2.
  3. Albo * m2 jest publiczny, chroniony lub zadeklarowany z domyślnym dostępem w tym samym pakiecie co C, albo * m1 zastępuje metodę m3, m3 różną od m1, m3 różną od m2, tak że m3 zastępuje m2.
Andy Thomas
źródło
4

Tak, w Javie można pisać wirtualne „funkcje”.

RepDetec
źródło
1

W Javie wszystkie publiczne (nieprywatne) zmienne i funkcje są domyślnie wirtualne . Ponadto zmienne i funkcje używające słowa kluczowego final nie są wirtualne .

Parvej Ahmed
źródło
co rozumiesz przez „zmienne wirtualne”?
neoexpert