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.
- ^ Miller, Dave (6 augusti 1999). "Java Tips 76: Ett alternativ till djupkopieringstekniken" . JavaWorld . Hämtad 2020-07-14 .
- ^ Clone() vs Copy constructor- som rekommenderas i java , StackOverflow
externa länkar
- McManus, Eamonn (4 april 2007). "Klona Java-objekt med serialisering" . Eamonn McManus blogg . java.net. Arkiverad från originalet den 13 augusti 2010 . Hämtad 2010-11-16 .
- Bloch, Joshua (2008). Effektiv Java: En programmeringsspråkguide . Java-serien (2:a upplagan). Addison-Wesley. ISBN 0-321-35668-3 .
- "Undvik klon" . Samlade Java-övningar . Hirondelle Systems. 2009 . Hämtad 2009-07-31 .
- "Objekt (Java Platform SE 6)" . Java Platform Standard Ed. 6 . Sun Microsystems, Inc. 2008 . Hämtad 2009-07-31 .
- Roulo, Mark (1 januari 1999). "Hur man undviker fällor och korrekt åsidosätter metoder från java.lang.Object" . JavaWorld . Hämtad 2020-07-14 . - Täcker grunderna för att implementera klonmetoden.
- Handledning för Java-kloning .