{"id":105,"date":"2009-01-17T02:07:21","date_gmt":"2009-01-17T01:07:21","guid":{"rendered":"http:\/\/blog.adrianlang.de\/?p=105"},"modified":"2009-01-17T02:07:21","modified_gmt":"2009-01-17T01:07:21","slug":"geklonter-kaffee-ist-kalt","status":"publish","type":"post","link":"https:\/\/blog.adrianheine.de\/?p=105","title":{"rendered":"Geklonter Kaffee ist kalt"},"content":{"rendered":"<p>Eine unabh\u00e4ngige Kopie eines Objekts zu erhalten ist keine triviale Angelegenheit. Die zentralen Ans\u00e4tze in C++, C# und Java, diese Funktionalit\u00e4t zu realisieren, sind die Methoden \u201eClone\u201c (C#) bzw. \u201e<a href=\"http:\/\/en.wikipedia.org\/wiki\/Clone_(Java_method)\" title=\"Wikipedia: \u201eclone\u201c\">clone<\/a>\u201c (Java) und <a href=\"http:\/\/en.wikipedia.org\/wiki\/Copy_constructor\"  title=\"Wikipedia: \u201eCopy constructor\u201c\">copy-Konstruktoren<\/a>.<!--more--> Der folgende Beitrag konzentriert sich auf die \u201eclone\u201c-Methode in Java.<\/p>\n<p>Das \u201eclone\u201c-Konzept in Java besteht aus drei verschiedenen Komponenten: dem Interface \u201ejava.lang.Cloneable\u201c, der Exception \u201ejava.lang.CloneNotSupportedException\u201c und der Methode \u201eclone\u201c in \u201ejava.lang.Object\u201c. Die Methode \u201eclone\u201c ist als \u201eprotected\u201c deklariert und vermutlich nicht in Java selbst implementiert, sondern \u201enative\u201c. Sie kopiert alle Felder des Objekts in ein neues Objekt. Es wird kein Konstruktor f\u00fcr das neue Objekt aufgerufen, und die einzelnen Felder werden nicht rekursiv kopiert, sondern einfach zugewiesen. Die Methode \u00fcberpr\u00fcft, ob das aktuelle Objekt das (leere) Marker-Interface \u201ejava.lang.Cloneable\u201c implementiert. Wenn nicht, wird die Exception ausgel\u00f6st. Eine Klasse, die das Interface implementiert, sollte die Methode mit einer \u201epublic\u201c-Methode \u00fcberschreiben und \u201esuper.clone()\u201c aufrufen.<\/p>\n<p>Soviel zur trockenen Beschreibung. Jetzt schauen wir mal, warum das Konzept \u201ebroken by design\u201c ist: Das Interface \u201ejava.lang.Cloneable\u201c deklariert keine \u201eclone\u201c-Methode, erst recht keine \u00f6ffentliche. Das hei\u00dft ganz einfach, dass folgender Code nicht kompiliert:<\/p>\n<pre>\r\npublic class MyUsefulClass {\r\n  public static Cloneable[] cloneMyArray(Cloneable[] myArray) {\r\n    int len = myArray.length;\r\n    Cloneable[] buf = new Cloneable[len];\r\n    for (int i = 0 ; i < len ; ++i) {\r\n      buf[i] = myArray[i].clone();\r\n    }\r\n    return buf;\r\n  }\r\n}\r\n<\/pre>\n<p>Nur Klassen und Interfaces, die ausdr\u00fccklich eine \u00f6ffentliche \u201eclone\u201c-Methode deklarieren, k\u00f6nnen in solchen Ausdr\u00fccken stehen.<\/p>\n<p>Soweit so schrecklich, aber es geht noch weiter. Java verwendet teilweise \u201eChecked exceptions\u201c, das hei\u00dft wenn eine Methode ausgef\u00fchrt wird, die eine Exception ausl\u00f6sen kann, muss die aufrufende Methode diese behandeln oder als die Exception ausl\u00f6send deklariert sein, so dass die n\u00e4chsth\u00f6here Ebene sich darum k\u00fcmmern muss. Ein durchaus interessantes Konzept mit Vor- und Nachteilen, im konkreten Fall aber eine ziemliche Katastrophe. Da die \u201eclone\u201c-Methode in \u201ejava.lang.Object\u201c als \u201ethrows java.lang.CloneNotSupportedException\u201c deklariert ist, m\u00fcssen alle sie aufrufenden Methoden (also z. B. alle \u201eclone\u201c-Methoden in abgeleiteten Klassen) diese Exception behandeln oder sie durchreichen. Da \u201ejava.lang.Object.clone\u201c aber nur dann diese Exception ausl\u00f6st, wenn das aktuelle Objekt nicht \u201ejava.lang.Cloneable\u201c implementiert, kann sich jede \u201eclone\u201c-Methode in einer Klasse, die direkt oder indirekt das Interface implementiert ziemlich sicher sein, dass sie mit dieser Exception nichts zu tun haben wird. <\/p>\n<p>Selbstverst\u00e4ndlich gibt es auch davon eine Ausnahme, und damit sind wir auch schon beim n\u00e4chsten gro\u00dfen Design-Problem: Ein einmal in einer Klasse implementiertes Interface klebt an jeder Unterklasse wie ein Kaugummi im Teppich. Wenn eine Klasse also partout nicht geklont werden m\u00f6chte, obwohl eine ihrer Superklassen \u201ejava.lang.Cloneable\u201c implementiert, muss sie eine eigene \u201eclone\u201c-Methode implementieren und in dieser eine Exception werfen.<\/p>\n<p>Ja, und dann gibt es noch ein paar unsch\u00f6ne Kleinigkeiten: \u201eclone\u201c gibt immer eine \u201ejava.lang.Object\u201c-Referenz zur\u00fcck, die erst mal gecastet werden muss. Au\u00dferdem muss die Methode auf F\u00e4higkeiten der Virtuellen Maschine zur\u00fcckgreifen, die eine Implementierung in Java sehr schwer bis unm\u00f6glich machen. Und schlie\u00dflich funktioniert klonen nicht wirklich gut mit \u201efinal\u201c-Feldern.<\/p>\n<p>Also noch mal zusammengefasst: Das Clone-Design in Java erm\u00f6glicht es, dass eine Klasse \u201ejava.lang.Cloneable\u201c implementiert aber keine \u201eclone\u201c-Methode hat und zwingt Klassen, die wissen, dass keine Exception auftreten kann zu einer Exception-Behandlung.<\/p>\n<h3>Links<\/h3>\n<ul>\n<li>Bill Venners: \u201e<a href=\"http:\/\/www.artima.com\/intv\/bloch13.html\" title=\"Bill Venners: \u201eCopy Constructor versus Cloning\u201c in \u201eJosh Bloch on Design\u201c\">Copy Constructor versus Cloning<\/a>\u201c in \u201eJosh Bloch on Design\u201c \u2013 Interview mit Josh Bloch\n<li>Shawn A. Van Ness: \u201e<a href=\"http:\/\/www.windojitsu.com\/blog\/copyctorvsicloneable.html\" title=\"Shawn A. Van Ness: \u201eCopy Constructors vs ICloneable -- Redux (Updated!)\u201c\">Copy Constructors vs ICloneable -- Redux (Updated!)<\/a>\u201c \u2013 Kritik an der C#-Variante IClonable<\/li>\n<li>\nAngelika Langer: \u201e <a href=\"http:\/\/www.angelikalanger.com\/Articles\/EffectiveJava\/07.Clone-Part3\/07.Clone-Part3.html\" title=\"Angelika Langer: \u201eDas Kopieren von Objekten in Java. Teil 3: Die CloneNotSupportedException\u201c\">Das Kopieren von Objekten in Java. Teil 3: Die CloneNotSupportedException<\/a>\u201c<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Eine unabh\u00e4ngige Kopie eines Objekts zu erhalten ist keine triviale Angelegenheit. Die zentralen Ans\u00e4tze in C++, C# und Java, diese Funktionalit\u00e4t zu realisieren, sind die Methoden \u201eClone\u201c (C#) bzw. \u201eclone\u201c (Java) und copy-Konstruktoren.<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[16],"tags":[74,70,71,76,75,73,69,72],"_links":{"self":[{"href":"https:\/\/blog.adrianheine.de\/index.php?rest_route=\/wp\/v2\/posts\/105"}],"collection":[{"href":"https:\/\/blog.adrianheine.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.adrianheine.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.adrianheine.de\/index.php?rest_route=\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.adrianheine.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=105"}],"version-history":[{"count":13,"href":"https:\/\/blog.adrianheine.de\/index.php?rest_route=\/wp\/v2\/posts\/105\/revisions"}],"predecessor-version":[{"id":119,"href":"https:\/\/blog.adrianheine.de\/index.php?rest_route=\/wp\/v2\/posts\/105\/revisions\/119"}],"wp:attachment":[{"href":"https:\/\/blog.adrianheine.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=105"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.adrianheine.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=105"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.adrianheine.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=105"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}