klon (Java-metod)

clone() är en metod i programmeringsspråket Java för objektduplicering . I Java manipuleras objekt genom referensvariabler, och det finns ingen operator för att kopiera ett objekt – tilldelningsoperatorn duplicerar referensen, inte objektet. Metoden clone() tillhandahåller denna saknade funktionalitet.

Översikt

Klasser som vill ha kopieringsfunktioner måste implementera någon metod för att göra det. Till viss del tillhandahålls den funktionen av " Object.clone() ".

clone() fungerar som en kopieringskonstruktor. Vanligtvis anropar den metoden clone() för sin superklass för att erhålla kopian, etc. tills den så småningom når Objects clone()- metod. Den speciella clone() -metoden i basklassen Object tillhandahåller en standardmekanism för att duplicera objekt.

Klassen Objects clone() -metod skapar och returnerar en kopia av objektet, med samma klass och med alla fält som har samma värden . Men Object.clone() kastar en CloneNotSupportedException om inte objektet är en instans av en klass som implementerar markörgränssnittet Cloneable .

Standardimplementeringen av Object.clone() utför en ytlig kopia . När en klass önskar en djup kopia eller något annat anpassat beteende, måste de implementera det i sin egen clone()- metod efter att de fått kopian från superklassen.

Syntaxen för att anropa clone i Java är (förutsatt att obj är en variabel av en klasstyp som har en public clone()- metod):

    Objektkopia  =  obj  .  _  klon  (); 

eller vanligt

     MyClass  copy  =  (  MyClass  )  obj  .  klon  (); 

som ger den typcasting som behövs för att tilldela den allmänna objektreferensen som returneras från klonen till en referens till ett MyClass- objekt.

En nackdel med utformningen av clone() -metoden är att returtypen för clone() är Object , och måste explicit castas tillbaka till lämplig typ. Men att åsidosätta clone() för att returnera lämplig typ är att föredra och eliminerar behovet av casting i klienten (med hjälp av kovarianta returtyper, sedan J2SE 5.0).

En annan nackdel är att man ofta inte kan komma åt metoden clone() på en abstrakt typ. De flesta gränssnitt och abstrakta klasser i Java anger inte en public clone()- metod. Som ett resultat kan metoden clone() ofta endast användas om den faktiska klassen för ett objekt är känd, vilket strider mot abstraktionsprincipen att använda den mest generiska typen som möjligt. Till exempel, om man har en Listreferens i Java, kan man inte anropa clone() på den referensen eftersom List inte anger någon public clone()- metod. Faktiska implementeringar av List som ArrayList och LinkedList har alla i allmänhet clone() metoder själva, men det är obekvämt och dålig abstraktion att bära runt den faktiska klasstypen för ett objekt.

Alternativ

Det finns alternativ till clone() , särskilt användningen av en kopieringskonstruktor - en konstruktor som accepterar en annan instans av samma klass som en parameter - eller en fabriksmetod . Dessa metoder är inte alltid tillräckliga när den konkreta typen av det klonade objektet inte är känd i förväg. (Men clone() är ofta inte adekvat heller av samma anledning, eftersom de flesta abstrakta klasser inte implementerar en public clone()- metod.)

Användningen av serialisering och deserialisering är också ett alternativ till att använda klon.

Singleton mönster

När du skriver en klass med Singleton-mönstret kan endast en instans av den klassen existera åt gången. Som ett resultat får klassen inte tillåtas att göra en klon. För att förhindra detta kan man åsidosätta clone() med följande kod:

     
      
 public  Object  clone  ()  kastar  CloneNotSupportedException  {  throw  new  CloneNotSupportedException  ();  } 

Detta är bara nödvändigt om en superklass implementerar en public clone()- metod, eller för att förhindra en underklass från att använda denna klasss clone()- metod för att få en kopia. Klasser ärver vanligtvis inte en public clone()- metod eftersom Object inte har en public clone()- metod, så det är vanligtvis onödigt att explicit implementera en icke-funktionell clone()- metod.

Klasshierarki

För att tillhandahålla ett korrekt kloningsbart objekt av vilken typ som helst måste metoden clone() både deklareras korrekt och implementeras korrekt enligt konventionen som beskrivs i Object.clone().

1) Varje typ som behöver klonas måste ha en public clone()-metod i sin egen klass eller en allmänt tillgänglig clone()-metod i en av sina överordnade klasser.

Exempel:

För att anropa clone() på varY1, som är av typ Y, måste Y eller en förälder till Y deklarera en allmänt tillgänglig clone()-metod. Här är det överordnade klassen X som tillhandahåller public clone()-metoden.

     
             
                  
        


      

      

   
             
                    
                    
        
 public  class  X  implementerar  Cloneable  {  public  X  clone  ()  kastar  CloneNotSupportedException  {  retur  (  X  )  super  .  klon  ();  }  }  public  class  Y  utökar  X  {  }  public  class  Z  utökar  Y  {  }  public  class  test1  {  public  void  function  ()  kastar  CloneNotSupportedException  {  Y  varY1  =  new  Z  ();  Y  varY2  =  (  Y  )  varY1  .  klon  ();  }  } 

2) Varje klass som implementerar clone() bör anropa super.clone() för att erhålla den klonade objektreferensen. Om klassen har några objektreferenser som också måste klonas (vid djupkopiering, till exempel), bör metoden clone() utföra alla nödvändiga ändringar av objektet innan det returneras. (Eftersom Object.clone() returnerar en exakt kopia av originalobjektet, skulle alla föränderliga fält som samlingar och arrayer delas mellan originalet och kopian - vilket i de flesta fall varken skulle förväntas eller önskas.)

Exempel:

Eftersom klass Z innehåller en objektreferens, klonar dess clone()-metod även den objektreferensen för att returnera en djup kopia av originalet.

     
             
                  
        


      

     
             
                  
        


     
          

             
                    
                  

                 
        


   
             
                    
                    
        
 public  class  X  implementerar  Cloneable  {  public  X  clone  ()  kastar  CloneNotSupportedException  {  retur  (  X  )  super  .  klon  ();  }  }  public  class  Y  utökar  X  {  }  public  class  ObjectABC  implementerar  Cloneable  {  public  ObjectABC  clone  ( )  kastar  CloneNotSupportedException  {  return  (  ObjectABC  )  super  .  klon  ();  }  }  public  class  Z  utökar  Y  {  private  ObjectABC  someABC  ;  public  Z  clone  ()  kastar  CloneNotSupportedException  {  Z  newZ  =  (  Z  )  super  .  klon  ();  newZ  .  someABC  =  someABC  .  klon  ();  returnera  nyZ  ;  }  }  public  class  test1  {  public  void  function  ()  kastar  CloneNotSupportedException  {  Y  varY1  =  new  Z  ();  Y  varY2  =  (  Y  )  varY1  .  klon  ();  }  } 

Fallgropar

Om varje klass i en hierarki implementerar en clone() -metod kommer alla dessa funktioner att anropas vid kloning, vilket lägger till en del overhead. Över många iterationer kan denna omkostnad bli betydande.

Med komplexa objektgrafer kan djupkopiering också bli problematiskt när rekursiva referenser finns.

Det är inte alltid lämpligt att ha flera kopior av samma objekt som flyter runt. Om syftet med en specifik clone()- implementering inte helt förstås av konsumenterna, kan det oavsiktligt bryta paradigmet "single object, multiple references".

Sista fälten

I allmänhet är clone() inkompatibel med slutliga fält. Eftersom clone() i huvudsak är en standardkonstruktor (en som inte har några argument) är det omöjligt att tilldela ett sista fält inom en clone()- metod; ett kompilatorfel är resultatet. Där värdet på fältet är ett oföränderligt objekt är detta okej; låt bara 'konstruktören' kopiera referensen och både originalet och dess klon kommer att dela samma objekt.

Men där värdet är ett föränderligt objekt måste det djupkopieras. En lösning är att ta bort den slutliga modifieraren från fältet, vilket ger upp de fördelar som modifieraren gav.

Av denna anledning föreslår vissa programmerare att göra objekten i hierarkin Serialiserbara , och skapa kopior genom att serialisera det gamla objektet och sedan skapa ett nytt objekt från den resulterande bitströmmen , som hanterar slutliga datamedlemmar korrekt, men är betydligt långsammare.

Alternativt kan man returnera ett helt nytt objekt från de aktuella objektfälten, vilket kan göras genom att först anropa konstruktorn och senare tilldela icke-slutliga fält. En annan alternativ metod är att faktiskt göra idén formell: att skapa en kopiakonstruktor som tar en instans. Det är faktiskt vad som rekommenderas framför kloning av vissa människor.

  1. ^ Miller, Dave (6 augusti 1999). "Java Tips 76: Ett alternativ till djupkopieringstekniken" . JavaWorld . Hämtad 2020-07-14 .
  2. ^ Clone() vs Copy constructor- som rekommenderas i java , StackOverflow

externa länkar