Sök…


Introduktion

Arv är en grundläggande objektorienterad funktion där en klass förvärvar och sträcker sig över egenskaperna hos en annan klass med hjälp av sökordet extends . För gränssnitt och sökord implements , se gränssnitt .

Syntax

  • klass ClassB utökar ClassA {...}
  • klass ClassB implementerar InterfaceA {...}
  • interface InterfaceB utökar InterfaceA {...}
  • klass ClassB utökar ClassA implementerar InterfaceC, InterfaceD {...}
  • abstrakt klass AbstractClassB utökar ClassA {...}
  • abstrakt klass AbstractClassB utökar AbstractClassA {...}
  • abstrakt klass AbstractClassB utökar ClassA implementerar InterfaceC, InterfaceD {...}

Anmärkningar

Arv kombineras ofta med generika så att basklassen har en eller flera typparametrar. Se Skapa en generisk klass .

Abstrakta klasser

En abstrakt klass är en klass markerad med det abstract nyckelordet. Det, till skillnad från icke-abstrakt klass, kan innehålla abstrakta - implementeringslösa - metoder. Det är dock giltigt att skapa en abstrakt klass utan abstrakta metoder.

En abstrakt klass kan inte instanseras. Det kan underklassas (utökas) så länge underklassen antingen också är abstrakt, eller implementerar alla metoder som är markerade som abstrakta av superklasser.

Ett exempel på en abstrakt klass:

public abstract class Component {
    private int x, y;
    
    public setPosition(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public abstract void render();
}

Klassen måste vara markerad abstrakt när den har minst en abstrakt metod. En abstrakt metod är en metod som inte har någon implementering. Andra metoder kan deklareras inom en abstrakt klass som har implementering för att tillhandahålla gemensam kod för alla underklasser.

Att försöka instansera den här klassen ger ett sammanställningsfel:

//error: Component is abstract; cannot be instantiated   
Component myComponent = new Component();

Men en klass som utvidgar Component och ger en implementering av alla dess abstrakta metoder och kan instanseras.

public class Button extends Component {

    @Override
    public void render() {
        //render a button
    }
}

public class TextBox extends Component {

    @Override
    public void render() {
        //render a textbox
    }
}

Instanser av att ärva klasser kan också gjutas som förälderklassen (normal arv) och de ger en polymorf effekt när den abstrakta metoden kallas.

Component myButton = new Button();
Component myTextBox = new TextBox();

myButton.render(); //renders a button
myTextBox.render(); //renders a text box

Abstrakta klasser vs gränssnitt

Abstrakta klasser och gränssnitt ger båda ett sätt att definiera metodsignaturer medan de kräver att den utvidgade / implementerande klassen tillhandahåller implementeringen.

Det finns två viktiga skillnader mellan abstrakta klasser och gränssnitt:

  • En klass får bara utöka en enda klass, men kan implementera många gränssnitt.
  • En abstrakt klass kan innehålla instansfält (icke- static ) fält, men gränssnitt kan endast innehålla static fält.
Java SE 8

Metoder som deklarerats i gränssnitt kunde inte innehålla implementationer, så abstrakta klasser användes när det var användbart att tillhandahålla ytterligare metoder som implementationer kallade abstrakta metoder.

Java SE 8

Java 8 tillåter gränssnitt att innehålla standardmetoder , vanligtvis implementerade med hjälp av de andra metoderna i gränssnittet , vilket gör gränssnitt och abstrakta klasser lika kraftfulla i detta avseende.

Anonyma underklasser av abstrakta klasser

Som bekvämlighet möjliggör java instans för anonyma instanser av underklasser av abstrakta klasser, som ger implementationer för de abstrakta metoderna vid skapandet av det nya objektet. Med exemplet ovan kan det se ut så här:

Component myAnonymousComponent = new Component() {
    @Override
    public void render() {
        // render a quick 1-time use component
    }
}

Statisk arv

Statisk metod kan ärvas liknar normala metoder, men till skillnad från normala metoder är det omöjligt att skapa " abstrakta " metoder för att tvinga statisk metodöverskridande. Att skriva en metod med samma signatur som en statisk metod i en superklass verkar vara en form av åsidosättande, men det skapar helt enkelt en ny funktion som döljer den andra.

public class BaseClass {
    
    public static int num = 5;

    public static void sayHello() {
        System.out.println("Hello");
    }

    public static void main(String[] args) {
        BaseClass.sayHello();
        System.out.println("BaseClass's num: " + BaseClass.num);
            
        SubClass.sayHello();
        //This will be different than the above statement's output, since it runs
        //A different method
        SubClass.sayHello(true);
        
        StaticOverride.sayHello();
        System.out.println("StaticOverride's num: " + StaticOverride.num);
    }
}

public  class SubClass extends BaseClass {
    
    //Inherits the sayHello function, but does not override it   
    public static void sayHello(boolean test) {
        System.out.println("Hey");
    }
}

public static class StaticOverride extends BaseClass {

    //Hides the num field from BaseClass
    //You can even change the type, since this doesn't affect the signature
    public static String num = "test";
        
    //Cannot use @Override annotation, since this is static
    //This overrides the sayHello method from BaseClass
    public static void sayHello() {
        System.out.println("Static says Hi");
    }

}

Att köra någon av dessa klasser ger resultatet:

Hello
BaseClass's num: 5
Hello
Hey
Static says Hi
StaticOverride's num: test

Observera att till skillnad från normal arv inte är dolda metoder i statisk arv. Du kan alltid ringa bas- sayHello metoden med hjälp av BaseClass.sayHello() . Men klasser ärver statiska metoder om inga metoder med samma signatur finns i underklassen. Om två metoders signaturer varierar, kan båda metoderna köras från underklassen, även om namnet är detsamma.

Statiska fält döljer varandra på liknande sätt.

Använda "final" för att begränsa arv och åsidosätta

Slutklasser

När den används i en class deklaration, vars final förhindrar modifierings andra klasser från att förklarade att extend klassen. En final klass är en "blad" -klass i arvsklasshierarkin.

// This declares a final class
final class MyFinalClass {
    /* some code */
}

// Compilation error: cannot inherit from final MyFinalClass
class MySubClass extends MyFinalClass {
    /* more code */
}

Användningsfall för slutklasser

Slutklasser kan kombineras med en private konstruktör för att kontrollera eller förhindra instans av en klass. Detta kan användas för att skapa en så kallad "utility class" som endast definierar statiska medlemmar; dvs. konstanter och statiska metoder.

public final class UtilityClass {

    // Private constructor to replace the default visible constructor
    private UtilityClass() {}

    // Static members can still be used as usual
    public static int doSomethingCool() {
        return 123;
    }

}

Oändliga klasser bör också förklaras som final . (En immutable klass är en vars instanser inte kan ändras efter att de har skapats; se ämnet I mmutable Objects .) Genom att göra detta gör du det omöjligt att skapa en muterbar underklass av en immutable klass. Det skulle bryta mot Liskovs substitutionsprincip som kräver att en subtyp ska följa "beteendekontraktet" för dess supertyper.

Ur ett praktiskt perspektiv är det lättare att resonera om programbeteende att förklara att en oföränderlig klass är final . Den tar också upp säkerhetsproblem i scenariot där otillförlitlig kod körs i en säkerhetssandlåda. (Till exempel, eftersom String förklaras som final , behöver en betrodd klass inte oroa sig för att det kan luras att acceptera muterbara underklasser, som den opålitliga anroparen sedan skulle kunna förändra.)

En nackdel med final klasser är att de arbetar inte med några hånfulla ramar som Mockito. Uppdatering: Mockito version 2 stöder nu hån mot slutklasser.

Slutliga metoder

Den final modifieraren kan också tillämpas på metoder för att förhindra att de åsidosätts i underklasser:

public class MyClassWithFinalMethod {

    public final void someMethod() {
    }
}

public class MySubClass extends MyClassWithFinalMethod {

    @Override
    public void someMethod() { // Compiler error (overridden method is final)
    }
}

Slutliga metoder används vanligtvis när du vill begränsa vad en underklass kan förändras i en klass utan att förbjuda underklasser helt.


Den final modifieraren kan också tillämpas på variabler, men betydelsen av final för variabler är inte relaterad till arv.

Liskovs substitutionsprincip

Substituerbarhet är en princip i objektorienterad programmering som introducerades av Barbara Liskov i en konferensnyckel från 1987 som säger att, om klass B är en underklass av klass A , varhelst A förväntas, kan B användas istället:

class A {...}
class B extends A {...}

public void method(A obj) {...}

A a = new B(); // Assignment OK
method(new B()); // Passing as parameter OK

Detta gäller också när typen är ett gränssnitt, där det inte behövs något hierarkiskt förhållande mellan objekten:

interface Foo {
    void bar();
}

class A implements Foo {
    void bar() {...}
}

class B implements Foo {
    void bar() {...}
}

List<Foo> foos = new ArrayList<>();
foos.add(new A()); // OK
foos.add(new B()); // OK

Nu innehåller listan objekt som inte kommer från samma klasshierarki.

Arv

Med användandet av nyckelordet extends bland klasserna finns alla egenskaper i superklassen (även känd som föräldraklass eller basklass ) i underklassen (även känd som barnklass eller härledd klass )

public class BaseClass {

    public void baseMethod(){
        System.out.println("Doing base class stuff");
    }
}

public class SubClass extends BaseClass {

}

Instanser av SubClass har ärvt metoden baseMethod() :

SubClass s = new SubClass();
s.baseMethod();  //Valid, prints "Doing base class stuff"

Ytterligare innehåll kan läggas till i en underklass. Genom att göra det möjliggörs ytterligare funktionalitet i underklassen utan någon ändring av basklassen eller andra underklasser från samma basklass:

public class Subclass2 extends BaseClass {

    public void anotherMethod() {
        System.out.println("Doing subclass2 stuff");
    }
}

Subclass2 s2 = new Subclass2();
s2.baseMethod(); //Still valid , prints "Doing base class stuff"
s2.anotherMethod(); //Also valid, prints "Doing subclass2 stuff" 

Fält ärvs också:

public class BaseClassWithField {

    public int x;

}

public class SubClassWithField extends BaseClassWithField {

    public SubClassWithField(int x) {
        this.x = x; //Can access fields
    }
}

private fält och metoder finns fortfarande inom underklassen, men är inte tillgängliga:

public class BaseClassWithPrivateField {

    private int x = 5;

    public int getX() {
        return x;
    }
}

public class SubClassInheritsPrivateField extends BaseClassWithPrivateField {

    public void printX() {
        System.out.println(x); //Illegal, can't access private field x
        System.out.println(getX()); //Legal, prints 5
    }
}

SubClassInheritsPrivateField s = new SubClassInheritsPrivateField();
int x = s.getX(); //x will have a value of 5.

I Java kan varje klass förlänga högst en annan klass.

public class A{}
public class B{}
public class ExtendsTwoClasses extends A, B {} //Illegal

Detta kallas flera arv, och även om det är lagligt på vissa språk tillåter Java det inte med klasser.

Som ett resultat av detta har varje klass en grenad förfäderkedja av klasser som leder till Object , från vilka alla klasser kommer ner.

Arv och statiska metoder

I Java kan båda föräldrar och barn ha statiska metoder med samma namn. Men i sådana fall döljer implementering av statisk metod hos barn föräldraklassens implementering, det är inte metodöverskridande. Till exempel:

class StaticMethodTest {

  // static method and inheritance
  public static void main(String[] args) {
    Parent p = new Child();
    p.staticMethod(); // prints Inside Parent
    ((Child) p).staticMethod(); // prints Inside Child
  }

  static class Parent {
    public static void staticMethod() {
      System.out.println("Inside Parent");
    }
  }

  static class Child extends Parent {
    public static void staticMethod() {
      System.out.println("Inside Child");
    }
  }
}

Statiska metoder är bindande till en klass inte till en instans och denna metodbindning sker vid sammanställningstiden. Eftersom i det första samtalet till staticMethod() användes föräldraklassreferens p användes Parent version av staticMethod() . I det andra fallet kastade vi p till Child class, Child 's staticMethod() exekverade.

Variabel skuggning

Variabler är SHADOWED och metoderna är ÖVERRIDDEN. Vilken variabel som kommer att användas beror på den klass som variabeln deklareras av. Vilken metod som kommer att användas beror på den faktiska klassen för objektet som referensen anges av variabeln.

class Car {
    public int gearRatio = 8;

    public String accelerate() {
        return "Accelerate : Car";
    }
}

class SportsCar extends Car {
    public int gearRatio = 9;

    public String accelerate() {
        return "Accelerate : SportsCar";
    }

    public void test() {

    }


    public static void main(String[] args) {

        Car car = new SportsCar();
        System.out.println(car.gearRatio + "  " + car.accelerate());
        // will print out 8  Accelerate : SportsCar
    }
}

Begränsa och bredda objektreferenser

Kasta en instans av en basklass till en underklass som i: b = (B) a; kallas förträngning (eftersom du försöker begränsa basklassobjektet till ett mer specifikt klassobjekt) och behöver ett uttryckligt typspel.

Kasta en instans av en underklass till en basklass som i: A a = b; kallas vidgning och behöver inte en typgjutning.

För att illustrera, överväga följande klassdeklarationer och testkod:

class Vehicle {
}

class Car extends Vehicle {
}

class Truck extends Vehicle {
}

class MotorCycle extends Vehicle {
}

class Test {

    public static void main(String[] args) {
    
        Vehicle vehicle = new Car();
        Car car = new Car();        
    
        vehicle = car; // is valid, no cast needed

        Car c = vehicle // not valid
        Car c = (Car) vehicle; //valid
    }
}

Uttalandet Vehicle vehicle = new Car(); är ett giltigt Java-uttalande. Varje instans av Car är också ett Vehicle . Därför är uppdraget lagligt utan behov av en uttrycklig typbesättning.

Å andra sidan, Car c = vehicle; är inte giltigt. Den statiska typen av vehicle är Vehicle vilket betyder att det kan hänvisa till en instans av Car , Truck , MotorCycle , or any other current or future subclass of fordon . (Or indeed, an instance of fordonet itself, since we did not declare it as an abstrakt class.) The assignment cannot be allowed, since that might lead to bil referring to a lastbilsinstans.

För att förhindra denna situation måste vi lägga till ett uttryckligt typspel:

Car c = (Car) vehicle;

Typgjutningen berättar kompilatorn att vi förväntar oss att vehicle värde är en Car eller en underklass av Car . Om det behövs sätter kompilatorn in kod för att utföra en typ av kontroll av körtid. Om kontrollen misslyckas ClassCastException en ClassCastException när koden körs.

Observera att inte alla typsändningar är giltiga. Till exempel:

String s = (String) vehicle;  // not valid

Java-kompilatorn vet att en instans som är typkompatibel med Vehicle aldrig kan vara typkompatibel med String . Typbesättningen kunde aldrig lyckas, och JLS uppmanar att detta ger ett sammanställningsfel.

Programmering till ett gränssnitt

Tanken bakom programmering till ett gränssnitt är att basera koden främst på gränssnitt och endast använda konkreta klasser vid tidpunkten för inställning. I det här sammanhanget kommer god kod som hanterar t.ex. Java-samlingar att se ut så här (inte att metoden i sig är till någon nytta alls, bara illustration):

public <T> Set<T> toSet(Collection<T> collection) {
  return Sets.newHashSet(collection);
}

medan dålig kod kan se ut så här:

public <T> HashSet<T> toSet(ArrayList<T> collection) {
  return Sets.newHashSet(collection);
}

Inte bara den förstnämnda kan tillämpas på ett bredare val av argument, dess resultat kommer att vara mer kompatibla med kod som tillhandahålls av andra utvecklare som i allmänhet följer konceptet att programmera till ett gränssnitt. De viktigaste orsakerna till att använda det förstnämnda är emellertid:

  • för det mesta behöver inte och behöver inte så många detaljer som det konkreta genomförandet ger;
  • att hålla sig till ett gränssnitt tvingar renare kod och mindre hacks, som ännu en offentlig metod, läggs till i en klass som serverar ett visst scenario;
  • koden är mer testbar eftersom gränssnitt är lätt att håna;
  • slutligen hjälper konceptet även om bara en implementering förväntas (åtminstone för testbarhet).

Så hur kan man enkelt tillämpa begreppet programmering på ett gränssnitt när man skriver ny kod med tanke på en viss implementering? Ett alternativ som vi vanligtvis använder är en kombination av följande mönster:

  • programmering till ett gränssnitt
  • fabrik
  • byggare

Följande exempel baserat på dessa principer är en förenklad och avkortad version av en RPC-implementering skriven för ett antal olika protokoll:

public interface RemoteInvoker {
  <RQ, RS> CompletableFuture<RS> invoke(RQ request, Class<RS> responseClass);
}

Ovanstående gränssnitt är inte tänkt att instanseras direkt via en fabrik, utan vi får ytterligare mer konkreta gränssnitt, ett för HTTP-anrop och ett för AMQP, var och en har en fabrik och en byggare för att konstruera instanser, som i sin tur också är instanser av ovanstående gränssnitt:

public interface AmqpInvoker extends RemoteInvoker {
  static AmqpInvokerBuilder with(String instanceId, ConnectionFactory factory) {
    return new AmqpInvokerBuilder(instanceId, factory);
  }
}

Instanser av RemoteInvoker för användning med AMQP kan nu konstrueras lika enkelt som (eller mer involverade beroende på byggaren):

RemoteInvoker invoker = AmqpInvoker.with(instanceId, factory)
  .requestRouter(router)
  .build();

Och en åkallelse av en begäran är lika lätt som:

Response res = invoker.invoke(new Request(data), Response.class).get();

På grund av Java 8 som tillåter placering av statiska metoder direkt i gränssnitt har mellanfabriken blivit implicit i koden ovan ersatt med AmqpInvoker.with() . I Java före version 8 kan samma effekt uppnås med en inre Factory :

public interface AmqpInvoker extends RemoteInvoker {
  class Factory {
    public static AmqpInvokerBuilder with(String instanceId, ConnectionFactory factory) {
      return new AmqpInvokerBuilder(instanceId, factory);
    }
  }
}

Motsvarande instanser skulle då förvandlas till:

RemoteInvoker invoker = AmqpInvoker.Factory.with(instanceId, factory)
  .requestRouter(router)
  .build();

Byggaren som används ovan kan se ut så här (även om detta är en förenkling eftersom den faktiska tillåter att definiera upp till 15 parametrar som avviker från standardvärden). Observera att konstruktionen inte är offentlig, så den är specifikt användbar endast från ovanstående AmqpInvoker gränssnitt:

public class AmqpInvokerBuilder {
  ...
  AmqpInvokerBuilder(String instanceId, ConnectionFactory factory) {
    this.instanceId = instanceId;
    this.factory = factory;
  }

  public AmqpInvokerBuilder requestRouter(RequestRouter requestRouter) {
    this.requestRouter = requestRouter;
    return this;
  }

  public AmqpInvoker build() throws TimeoutException, IOException {
    return new AmqpInvokerImpl(instanceId, factory, requestRouter);
  }
}

I allmänhet kan en byggmästare också genereras med ett verktyg som FreeBuilder.

Slutligen definieras standard (och den enda förväntade) implementeringen av detta gränssnitt som en paketlokal klass för att upprätthålla användningen av gränssnittet, fabriken och byggaren:

class AmqpInvokerImpl implements AmqpInvoker {
  AmqpInvokerImpl(String instanceId, ConnectionFactory factory, RequestRouter requestRouter) {
    ...
  }

  @Override
  public <RQ, RS> CompletableFuture<RS> invoke(final RQ request, final Class<RS> respClass) {
    ...
  }
}

Samtidigt visade sig detta mönster vara mycket effektivt för att utveckla all vår nya kod oavsett hur enkel eller komplex funktionen är.

Abstrakt klass- och gränssnittsanvändning: "Is-a" relation vs "Har-a" förmåga

När man ska använda abstrakta klasser: Att implementera samma eller olika beteende bland flera relaterade objekt

När man ska använda gränssnitt: att implementera ett kontrakt med flera icke-relaterade objekt

Abstrakta klasser skapar "är en" relation medan gränssnitt ger "har" förmåga.

Detta kan ses i koden nedan:

public class InterfaceAndAbstractClassDemo{
    public static void main(String args[]){
        
        Dog dog = new Dog("Jack",16);
        Cat cat = new Cat("Joe",20);
            
        System.out.println("Dog:"+dog);
        System.out.println("Cat:"+cat);
        
        dog.remember();
        dog.protectOwner();
        Learn dl = dog;
        dl.learn();
                
        cat.remember();
        cat.protectOwner();
        
        Climb c = cat;
        c.climb();
        
        Man man = new Man("Ravindra",40);
        System.out.println(man);
        
        Climb cm = man;
        cm.climb();
        Think t = man;
        t.think();
        Learn l = man;
        l.learn();
        Apply a = man;
        a.apply();
    }
}

abstract class Animal{
    String name;
    int lifeExpentency;
    public Animal(String name,int lifeExpentency ){
        this.name = name;
        this.lifeExpentency=lifeExpentency;
    }
    public abstract void remember();
    public abstract void protectOwner();
    
    public String toString(){
        return this.getClass().getSimpleName()+":"+name+":"+lifeExpentency;
    }
}
class Dog extends Animal implements Learn{
    
    public Dog(String name,int age){
        super(name,age);
    }
    public void remember(){
        System.out.println(this.getClass().getSimpleName()+" can remember for 5 minutes");
    }
    public void protectOwner(){
        System.out.println(this.getClass().getSimpleName()+ " will protect owner");
    }
    public void learn(){
        System.out.println(this.getClass().getSimpleName()+ " can learn:");
    }
}
class Cat extends Animal implements Climb {
    public Cat(String name,int age){
        super(name,age);
    }
    public void remember(){
        System.out.println(this.getClass().getSimpleName() + " can remember for 16 hours");
    }
    public void protectOwner(){
        System.out.println(this.getClass().getSimpleName()+ " won't protect owner");
    }
    public void climb(){
        System.out.println(this.getClass().getSimpleName()+ " can climb");
    }
}
interface Climb{
    void climb();
}
interface Think {
    void think();
}

interface Learn {
    void learn();
}
interface Apply{
    void apply();
}

class Man implements Think,Learn,Apply,Climb{
    String name;
    int age;

    public Man(String name,int age){
        this.name = name;
        this.age = age;
    }
    public void think(){
        System.out.println("I can think:"+this.getClass().getSimpleName());
    }
    public void learn(){
        System.out.println("I can learn:"+this.getClass().getSimpleName());
    }
    public void apply(){
        System.out.println("I can apply:"+this.getClass().getSimpleName());
    }
    public void climb(){
        System.out.println("I can climb:"+this.getClass().getSimpleName());
    }
    public String toString(){
        return "Man :"+name+":Age:"+age;
    }
}

produktion:

Dog:Dog:Jack:16
Cat:Cat:Joe:20
Dog can remember for 5 minutes
Dog will protect owner
Dog can learn:
Cat can remember for 16 hours
Cat won't protect owner
Cat can climb
Man :Ravindra:Age:40
I can climb:Man
I can think:Man
I can learn:Man
I can apply:Man

Viktiga anmärkningar:

  1. Animal är en abstrakt klass med delade attribut: name och lifeExpectancy och abstrakta metoder: kom remember() och protectOwner() . Dog och Cat är Animals som har implementerat metoderna remember() och protectOwner() .

  2. Cat kan climb() men Dog kan inte. Dog kan think() men Cat kan inte. Dessa specifika funktioner läggs till Cat and Dog genom implementering.

  3. Man är inte ett Animal men han kan Think , Learn , Apply och Climb .

  4. Cat är inte en Man men den kan Climb .

  5. Dog är inte en Man men den kan Learn

  6. Man är varken en Cat eller en Dog men kan ha några av de två senare kapaciteten utan att förlänga Animal , Cat eller Dog . Detta görs med gränssnitt.

  7. Även om Animal är en abstrakt klass, har det en konstruktör, till skillnad från ett gränssnitt.

TL; DR:

Orelaterade klasser kan ha kapacitet genom gränssnitt, men relaterade klasser ändrar beteendet genom att förlänga basklasser.

Se dokumentationen Java sida för att förstå vilken som ska användas i ett specifikt användningsfall.

Överväg att använda abstrakta klasser om ...

  1. Du vill dela kod mellan flera närbesläktade klasser.
  2. Du förväntar dig att klasser som utökar din abstrakta klass har många vanliga metoder eller fält eller kräver andra åtkomstmodifierare än offentliga (t.ex. skyddade och privata).
  3. Du vill förklara icke-statiska eller icke-slutliga fält.

Överväg att använda gränssnitt om ...

  1. Du förväntar dig att icke relaterade klasser skulle implementera ditt gränssnitt. Till exempel kan många icke-relaterade objekt implementera Serializable gränssnittet.
  2. Du vill ange beteendet för en viss datatyp men är inte bekymrad över vem som implementerar dess beteende.
  3. Du vill dra fördel av flera arv av typen.

Övergripande i arv

Överskridande i arv används när du använder en redan definierad metod från en superklass i en underklass, men på ett annat sätt än hur metoden ursprungligen utformades i superklassen. Överskridande gör att användaren kan återanvända koden genom att använda befintligt material och ändra den för att bättre passa användarens behov.


Följande exempel visar hur ClassB åsidosätter funktionaliteten för ClassA genom att ändra vad som skickas ut via utskriftsmetoden:

Exempel:

public static void main(String[] args) {
    ClassA a = new ClassA();
    ClassA b = new ClassB();
    a.printing();
    b.printing();
}

class ClassA {
    public void printing() {        
        System.out.println("A");
    }
}

class ClassB extends ClassA {
    public void printing() {
         System.out.println("B");
    }
}

Produktion:

EN

B



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow