Sommario 8 Ereditarietà ................................................................................................................................... 4 8.1 Un esempio per imparare ...................................................................................................... 4 8.2 Ereditarietà gerarchica ........................................................................................................... 5 Ereditarietà e regole di accessibilità............................................................................................. 6 Ereditarietà e inizializzazione degli oggetti ................................................................................. 7 8.3 Aggiungere un elemento alla gerarchia ................................................................................. 9 8.4 Vantaggi dell'ereditarietà ..................................................................................................... 10 8.5 Sottotipi ............................................................................................................................... 11 Sottotipi e assegnamenti............................................................................................................. 12 Sottotipi e passaggio dei parametri ............................................................................................ 13 Casting ....................................................................................................................................... 14 La classe Object................................................................................................................... 15 8.7 Autoboxing e classi wrapper ............................................................................................... 16 8.8 Per ripassare ........................................................................................................................ 17 9 8.6 Approfondimenti sull'ereditarietà .............................................................................................. 18 9.1 The problem: network’s display method ............................................................................. 18 9.2 Static type vs dynamic type ................................................................................................. 18 9.3 Overriding ........................................................................................................................... 20 9.4 Dynamic method lookup (late binding)............................................................................... 21 9.5 Super Call in methods ......................................................................................................... 21 9.6 Method polymorphism ........................................................................................................ 21 9.7 Object methods: toString ..................................................................................................... 21 9.8 Object equality: equals and hashCode................................................................................. 21 9.9 Java visibility modifiers ...................................................................................................... 22 Protected access ......................................................................................................................... 22 Controlling Access to Members of a Class ................................................................................ 22 9.10 Access Control and Inheritance: ...................................................................................... 23 9.11 The instanceof operator ................................................................................................... 24 9.12 Per ripassare ..................................................................................................................... 24 10 Classi astratte e interfacce .......................................................................................................... 24 11 Object as a Superclass ................................................................................................................ 25 11.1 Clonazione di oggetti ....................................................................................................... 26 Non tutti gli oggetti sono clonabili ............................................................................................ 26 The clone() Method .................................................................................................................... 26 Copia superficiale: un primo esempio ....................................................................................... 27 Clonazione superficiale in classi derivate .............................................................................. 27 Copia superficiale e profonda: differenze .................................................................................. 27 Esempio: Classe Segmento .................................................................................................... 28 Copia superficiale .................................................................................................................. 29 Copia profonda: significato .................................................................................................... 29 Copia profonda: realizzazione ............................................................................................... 30 11.2 Uguaglianza tra oggetti .................................................................................................... 31 Uguaglianza superficiale ............................................................................................................ 31 Uguaglianza profonda ................................................................................................................ 32 11.3 The finalize() Method ...................................................................................................... 32 11.4 The getClass() Method .................................................................................................... 33 11.5 The hashCode() Method .................................................................................................. 33 11.6 The toString() Method ..................................................................................................... 33 11.7 Confronto tra oggetti........................................................................................................ 34 L'interfaccia java.lang.Comparable ......................................................................... 34 Ordinamento di Array con java.util.Arrays.sort() ...................................................................... 37 Ordinamento di List con java.util.Collections ........................................................................... 38 Collections vs Collection ........................................................................................................... 39 Ordinamento mediante l'interfaccia java.util.Comparator ......................................................... 40 11.8 Classi Nested ................................................................................................................... 43 Osservazioni ........................................................................................................................... 43 Convertire un ArrayList in un Array ............................................................................... 48 11.10 Convertire ArrayList in Array ......................................................................................... 49 12 11.9 Interfacce Grafiche (GUI) .......................................................................................................... 49 12.1 Elementi di un'interfaccia grafica .................................................................................... 49 12.2 AWT e Swing .................................................................................................................. 50 12.3 La prima applicazione grafica ......................................................................................... 50 Aggiunta di menu ....................................................................................................................... 51 Gestione degli eventi .................................................................................................................. 52 Osservazione importante: meglio avere listeners distinti per oggetti distinti ........................ 54 Inner classes (rivisitate) ............................................................................................................. 54 Anonymous inner classes ........................................................................................................... 56 12.4 Un programma per la visualizzazione e modifica delle immagini: prima versione ........ 58 12.5 Layouts ............................................................................................................................ 62 BorderLayout ......................................................................................................................... 63 FlowLayout ............................................................................................................................ 63 GridLayout ............................................................................................................................. 64 BoxLayout .............................................................................................................................. 64 Nested Containers ...................................................................................................................... 65 12.6 Creazione di un'interfaccia grafica con NetBeans ........................................................... 66 Esercizio 1: Il “gioco del 15”. .................................................................................................... 67 Versione con numeri .............................................................................................................. 67 Versione con immagini “puzzlefy” ........................................................................................ 67 Esercizio 2: Emulare la calcolatrice standard di Windows ........................................................ 67 13 Gestione delle eccezioni (rivisitate) ........................................................................................... 69 Approfondimenti.............................................................................................................. 69 13.2 Logging delle eccezioni e di situazioni specifiche: java.util.logging package ................ 69 14 13.1 Gestione dei file in Java ............................................................................................................. 69 java.io.File ....................................................................................................................... 69 14.2 java.util.Formatter e java.util.Scanner; ............................................................................ 70 14.3 Scrivere su file di testo con PrintWriter e BufferedWriter una riga alla volta ................ 73 14.4 Leggere da file di testo una riga alla volta con BufferedReader ..................................... 74 14.5 Un esempio di lettura da file CSV ................................................................................... 74 14.6 Un esempio di scrittura su file CSV ................................................................................ 75 14.7 Un esempio di uso di PrintWriter e BufferedReader ....................................................... 76 14.8 Serializzazione di oggetti ................................................................................................. 77 14.9 Progetto di una rubrica di contatti ................................................................................... 78 15 14.1 Riferimenti per Java ................................................................................................................... 78 In queste note si seguirà l'approccio del testo Objects First with Java i cui esempi (utilizzati in queste note) sono liberamente scaricabili al link http://www.bluej.org/objects-first/resources/projects.zip . 8 Ereditarietà 8.1 Un esempio per imparare Apriamo il progetto network-v1: esploriamo il codice e poi proviamo a creare alcuni oggetti. Esercizio: discutere il codice e trovare eventuali problemi di progettazione. Il problema fondamentale è che le classi MessagePost e PhotoPost condividono molto codice comune. E se la nostra applicazione chiedesse di fare anche un ActivityPost (qualcosa che ci aggiorna sui nostri contatti...qualcosa del tipo “tizio è ora amico di...” Per risolvere il problema della duplicazione del codice possiamo utilizzare il concetto di ereditarietà: creiamo una classe Post che contiene gli attributi e i metodi comuni di tutti i post e poi deriviamo il MessagePost e il PhotoPost come classi che estendono la classe Post. “The purpose of using inheritance is now fairly obvious. Instances of class MessagePost will have all fields defined in class MessagePost and in class Post. (MessagePost inherits the fields from Post.) Instances of PhotoPost will have all fields defined in PhotoPost and in Post. Thus, we achieve the same as before, but we need to define the fields username, timestamp, likes, and comments only once, while being able to use them in two different places). The same holds true for methods: instances of subclasses have all methods defined in both the superclass and the subclass. In general, we can say: because a message post is a post, a message-post object has everything that a post has, and more. And because a photo post is also a post, it has everything that a post has, and more. Thus, inheritance allows us to create two classes that are quite similar, while avoiding the need to write the identical part twice. Inheritance has a number of other advantages, which we discuss below. First, however, we will take another, more general look at inheritance hierarchies. ” 8.2 Ereditarietà gerarchica The principle is simple: inheritance is an abstraction technique that lets us categorize classes of objects under certain criteria and helps us specify the characteristics of these classes. public class Post { private String username; // username of the post’s author private long timestamp; private int likes; private ArrayList<String> comments; } // Constructors and methods omitted. public class MessagePost extends Post { private String message; } // Constructors and methods omitted. public class PhotoPost extends Post { private String filename; private String caption; } // Constructors and methods omitted. Ereditarietà e regole di accessibilità Members defined as public in either the superclass or subclass portions will be accessible to objects of other classes, but members defined as private will be inaccessible. In fact, the rule on privacy also applies between a subclass and its superclass: a subclass cannot access private members of its superclass. It follows that if a subclass method needed to access or change private fields in its superclass, then the superclass would need to provide appropriate accessor and/or mutator methods. However, an object of a subclass may call any public methods defined in its superclass as if they were defined locally in the subclass—no variable is needed, because the methods are all part of the same object. Ereditarietà e inizializzazione degli oggetti public class Post { private String username; // username of the post’s author private long timestamp; private int likes; private ArrayList<String> comments; /** * Constructor for objects of class Post. * * @param author The username of the author of this post. */ } public Post(String author) { } username = author; timestamp = System.currentTimeMillis(); likes = 0; comments = new ArrayList<String>(); // Methods omitted. public class MessagePost extends Post { private String message; // an arbitrarily long, multi-line message /** * Constructor for objects of class MessagePost. * * @param author * @param text */ The username of the author of this post. The text of this post. } public MessagePost(String author, String text) { super(author); message = text; } // Methods omitted. First, the class Post has a constructor, even though we do not intend to create an instance of class Post directly. This constructor receives the parameters needed to initialize the Post fields, and it contains the code to do this initialization. Second, the MessagePost constructor receives parameters needed to initialize both Post and MessagePost fields. It then contains the following line of code: super(author); The keyword super is a call from the subclass constructor to the constructor of the superclass. Its effect is that the Post constructor is executed as part of the MessagePost constructor’s execution. When we create a message post, the MessagePost constructor is called, which, in turn, as its first statement, calls the Post constructor. The Post constructor initializes the post’s fields, and then returns to the MessagePost constructor, which initializes the remaining field defined in the MessagePost class. For this to work, those parameters needed for the initialization of the post fields are passed on to the superclass constructor as parameters to the super call. In Java, a subclass constructor must always call the superclass constructor as its first statement. If you do not write a call to a superclass constructor, the Java compiler will insert a superclass call automatically, to ensure that the superclass fields get properly initialized. The inserted call is equivalent to writing super(); Inserting this call automatically works only if the superclass has a constructor without parameters (because the compiler cannot guess what parameter values should be passed). Otherwise, an error will be reported. In general, it is a good idea to always include explicit superclass calls in your constructors, even if it is one that the compiler could generate automatically. We consider this good style, because it avoids the possibility of misinterpretation and confusion in case a reader is not aware of the automatic code generation. 8.3 Aggiungere un elemento alla gerarchia Aggiungere la classe EventPost con il campo eventType di tipo enumerativo. E se volessimo i commenti solo per i messaggi e le foto, ma non per gli eventi: Il progetto BlueJ con le modifiche dell'esercizio 8.8 diventa network-v2-modified: 8.4 Vantaggi dell'ereditarietà Avoiding code duplication The use of inheritance avoids the need to write identical or very similar copies of code twice (or even more often). Code reuse Existing code can be reused. If a class similar to the one we need already exists, we can sometimes subclass the existing class and reuse some of the existing code rather than having to implement everything again. Easier maintenance Maintaining the application becomes easier, because the relationship between the classes is clearly expressed. A change to a field or a method that is shared between different types of subclasses needs to be made only once. Extendibility Using inheritance, it becomes much easier to extend an existing application in certain ways. 8.5 Sottotipi Confrontiamo la classe NewsFeed del progetto network-v2 con la classe NewsFeed del progetto network-v1. Nella versione con ereditarietà il codice è molto più semplice perché abbiamo solo il tipo Post e possiamo scrivere metodi che aggiungono un post oppure mostrano il contenuto di un post, senza specificare se si tratta di MessagePost o di PhotoPost. public void addPost(Post post) { posts.add(post); } /** * Show the news feed. Currently: print the news feed details * to the terminal. (To do: replace this later with display * in web browser.) */ public void show() { // display all posts for(Post post : posts) { post.display(); System.out.println(); // empty line between posts } } In our first version, we had two methods to add posts to the news feed. They had the following headers: public void addMessagePost(MessagePost message) public void addPhotoPost(PhotoPost photo) In our new version, we have a single method to serve the same purpose: public void addPost(Post post) So far, we have interpreted the requirement that parameter types must match as meaning “must be of the same type”—for instance, that the type name of an actual parameter must be the same as the type name of the corresponding formal parameter. This is only part of the truth, in fact, because an object of a subclass can be used wherever its superclass type is required. Sottotipi e assegnamenti Imagine that we have a class Vehicle with two subclasses, Car and Bicycle (Figure 8.9). In this case, the typing rule admits that the following assignments are all legal: Vehicle v1 = new Vehicle(); Vehicle v2 = new Car(); Vehicle v3 = new Bicycle(); Because a car is a vehicle, it is perfectly legal to store a car in a variable that is intended for vehicles This principle is known as substitution. In object-oriented languages, we can substitute a subclass object where a superclass object is expected, because the subclass object is a special case of the superclass. If, for example, someone asks us to give them a pen, we can fulfill the request perfectly well by giving them a fountain pen or a ballpoint pen. Both fountain pen and ballpoint pen are subclasses of pen, so supplying either where an object of class However, doing it the other way is not allowed: Car c1 = new Vehicle(); // this is an error! This statement attempts to store a Vehicle object in a Car and an error will be reported if you try to compile this statement. A vehicle, on the other hand, may or may not be a car—we do not know. Thus, the statement may be wrong and is not allowed. Similarly: Car c2 = new Bicycle(); // this is an error! This is also an illegal statement. A bicycle is not a car Sottotipi e passaggio dei parametri public class NewsFeed { public void addPost(Post post) { . . . } } We can now use this method to add message posts and photo posts to the feed: NewsFeed feed = new NewsFeed(); MessagePost message = new MessagePost() PhotoPost photo = new PhotoPost(...); feed.addPost(message); feed.addPost(photo); Because of subtyping rules, we need only one method (with a parameter of type both MessagePost and PhotoPost objects. Casting Sometimes the rule that we cannot assign from a supertype to a subtype is more restrictive than necessary. If we know that the supertype variable holds a subtype object, the assignment could actually be allowed. For example: Vehicle v; Car c = new Car(); v = c; // correct c = v; // error The above statements would not compile: we get a compiler error in the last line, because assigning a Vehicle variable to a Car variable (supertype to subtype) is not allowed. However, if we execute these statements in sequence, we know that we could actually allow this assignment. We can see that the variable v actually contains an object of type Car, so the assignment to c would be okay. The compiler is not that smart. It translates the code line by line, so it looks at the last line in isolation without knowing what is currently stored in variable v. This is called type loss. The type of the object in v is actually Car, but the compiler does not know this. We can get around this problem by explicitly telling the type system that the variable v holds a Car object. We do this using a cast operator: c = (Car) v; // okay The cast operator consists of the name of a type (here, Car) written in parentheses in front of a variable or an expression. Doing this will cause the compiler to believe that the object is a Car, and it will not report an error. At runtime, however, the Java system will check that it really is a Car. If we were careful, and it is truly is a Car, everything is fine. If the object in v is of another type, the runtime system will indicate an error (called a ClassCastException), and the program will stop. Now consider this code fragment, in which Bicycle is also a subclass of Vehicle: Vehicle v; Car c; Bicycle b; c = new Car(); v = c; // okay b = (Bicycle) c; // compile time error! b = (Bicycle) v; // runtime error! The last two assignments will both fail. The attempt to assign c to b (even with the cast) will be a compile-time error. The compiler notices that Car and Bicycle do not form a subtype/supertype relationship, so c can never hold a Bicycle object—the assignment could never work. The attempt to assign v to b (with the cast) will be accepted at compile time but will fail at runtime. Vehicle is a superclass of Bicycle, and thus v can potentially hold a Bicycle object. At runtime, however, it turns out that the object in v is not a Bicycle but a Car, and the program will terminate prematurely. Casting should be avoided wherever possible, because it can lead to runtime errors, and that is clearly something we do not want. The compiler cannot help us to ensure correctness in this case. In practice, casting is very rarely needed in a well-structured object-oriented program. In almost all cases, when you use a cast in your code, you could restructure your code to avoid this cast and end up with a better-designed program. This usually involves replacing the cast with a polymorphic method call (more about this in the next chapter). 8.6 La classe Object All classes have a superclass. So far, it has appeared as if most classes we have seen do not have a superclass. In fact, while we can declare an explicit superclass for a class, all classes that have no superclass declaration implicitly inherit from a class called Object. Object is a class from the Java standard library that serves as a superclass for all objects. Writing a class declaration such as public class Person { ... } is equivalent to writing public class Person extends Object { ... } The Java compiler automatically inserts the Object superclass for all classes without an explicit extends declaration, so it is never necessary to do this for yourself. Every single class (with the sole exception of the Object class itself) inherits from Object, either directly or indirectly. The following figure shows some randomly chosen classes to illustrate this. 8.7 Autoboxing e classi wrapper We have seen that, with suitable parameterization, the collection classes can store objects of any object type. There remains one problem: Java has some types that are not object types. As we know, the simple types—such as int, boolean, and char—are separate from object types. Their values are not instances of classes, and they do not inherit from the Object class. Because of this, they are not subtypes of Object, and it would not normally be possible to add them into a collection. This is unfortunate. There are situations in which we might want to create a list of int values or a set of char values, for instance. What can we do? Java’s solution to this problem is wrapper classes. Every primitive type in Java has a corresponding wrapper class that represents the same type but is a real object type. The wrapper class for int, for example, is called Integer. The following statement explicitly wraps the value of the primitive int variable ix in an Integer object: Integer iwrap = new Integer(ix); And now iwrap could obviously easily be stored in an ArrayList<Integer> collection, for instance. However, storing of primitive values into an object collection is made even easier through a compiler feature known as autoboxing. Whenever a value of a primitive type is used in a context that requires a wrapper type, the compiler automatically wraps the primitive-type value in an appropriate wrapper object. This means that primitive-type values can be added directly to a collection: private ArrayList<Integer> markList; ... public void storeMarkInList(int mark) { markList.add(mark); } The reverse operation—unboxing—is also performed automatically, so retrieval from a collection might look like this: int firstMark = markList.remove(0); Autoboxing is also applied whenever a primitive-type value is passed as a parameter to a method that expects a wrapper type and when a primitive-type value is stored in a wrapper-type variable. Similarly, unboxing is applied when a wrapper-type value is passed as a parameter to a method that expects a primitive-type value and when stored in a primitive-type variable. It is worth noting that this almost makes it appear as if primitive types can be stored in collections. However, the type of the collection must still be declared using the wrapper type (e.g., ArrayList<Integer>, not ArrayList<int>). Esercizio: 8.8 Per ripassare 9 Approfondimenti sull'ereditarietà 9.1 The problem: network’s display method Vedere il libro 9.2 Static type vs dynamic type Prendiamo il progetto network-v2 e osserviamo il comportamento del metodo display Cosa succede se Post non ha il metodo display e nella classe NewsFeed si richiama il metodo /** * Show the news feed. Currently: print the news feed details * to the terminal. (To do: replace this later with display * in web browser.) */ public void show() { // display all posts for(Post post : posts) { post.display(); System.out.println(); // empty line between posts } } Quale metodo display è invocato nel ciclo for-each? We know that every Post object in the collection is in fact a MessagePost or a PhotoPost object, and both have display methods. This should mean that post.display() ought to work, because, whatever it is— MessagePost or PhotoPost—we know that it does have a display method. Consider the following statement: Vehicle v1 = new Car(); What is the type of v1? That depends on what precisely we mean by “type of v1.” The type of the variable v1 is Vehicle; the type of the object stored in v1 is Car. Through subtyping and substitution rules, we now have situations where the type of the variable and the type of the object stored in it are not exactly the same. Let us introduce some terminology to make it easier to talk about this issue: We call the declared type of the variable the static type, because it is declared in the source code— the static representation of the program. We call the type of the object stored in a variable the dynamic type, because it depends on assignments at runtime—the dynamic behavior of the program. Thus, looking at the explanations above, we can be more precise: the static type of v1 is Vehicle, the dynamic type of v1 is Car. We can now also rephrase our discussion about the call to the post’s display method in the NewsFeed class. At the time of the call post.display(); the static type of post is Post, while the dynamic type is either MessagePost or PhotoPost. We do not know which one of these it is, assuming that we have entered both MessagePost and PhotoPost objects into the feed. 9.3 Overriding Il progetto network-v3 fornisce un'implementazione delle classi secondo quanto descritto nell'esercizio 9.2 The technique we are using here is called overriding (sometimes it is also referred to as redefinition). Overriding is a situation where a method is defined in a superclass (method display in class Post in this example), and a method with exactly the same signature is defined in the subclass. In this situation, objects of the subclass have two methods with the same name and header: one inherited from the superclass and one from the subclass. Which one will be executed when we call this method? 9.4 Dynamic method lookup (late binding) Vedere §9.4 del libro. 9.5 Super Call in methods Vedere §9.5 del libro. 9.6 Method polymorphism Vedere §9.6 del libro. 9.7 Object methods: toString Vedere §9.7 del libro. 9.8 Object equality: equals and hashCode Esercizio: Creare due oggetti Track t1 = new Track(“artista”, “titolo canzone 1”, “file name 1”); Track t2 = new Track(“artista”, “titolo canzone 1”, “file name 1”); e verificare se il metodo equals restituisce vero o falso. t1.equals(t2) Ridefinire il metodo equals() nella classe Track in modo che verifichi se i campi (variabili d'istanza) siano a loro volta uguali. t1.equals(t2) Richiamare il metodo hashCode sugli oggetti t1 e t2 e commentare il risultato ottenuto. t1.hashCode() Eseguire le istruzioni System.out.println(t1); System.out.println(t2); commentare il risultato Ridefinire il metodo toString della classe Track in modo che presenti una descrizione completa di una traccia e rieseguire le istruzioni: System.out.println(t1); System.out.println(t2); 9.9 Java visibility modifiers http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html http://www.tutorialspoint.com/java/java_access_modifiers.htm Protected access Vedere §9.9 del libro. Controlling Access to Members of a Class Access level modifiers determine whether other classes can use a particular field or invoke a particular method. There are two levels of access control: At the top level—public, or package-private (no explicit modifier). At the member level—public, private, protected, or package-private (no explicit modifier). A class may be declared with the modifier public, in which case that class is visible to all classes everywhere. If a class has no modifier (the default, also known as package-private), it is visible only within its own package (packages are named groups of related classes — you will learn about them in a later lesson.) At the member level, you can also use the public modifier or no modifier (package-private) just as with top-level classes, and with the same meaning. For members, there are two additional access modifiers: private and protected. The private modifier specifies that the member can only be accessed in its own class. The protected modifier specifies that the member can only be accessed within its own package (as with package-private) and, in addition, by a subclass of its class in another package. The following table shows the access to members permitted by each modifier. Access Levels Modifier Class Package Subclass World public Y Y Y Y protected Y Y Y N no modifier Y Y N N private Y N N N The first data column indicates whether the class itself has access to the member defined by the access level. As you can see, a class always has access to its own members. The second column indicates whether classes in the same package as the class (regardless of their parentage) have access to the member. The third column indicates whether subclasses of the class declared outside this package have access to the member. The fourth column indicates whether all classes have access to the member. Access levels affect you in two ways. First, when you use classes that come from another source, such as the classes in the Java platform, access levels determine which members of those classes your own classes can use. Second, when you write a class, you need to decide what access level every member variable and every method in your class should have. Let's look at a collection of classes and see how access levels affect visibility. The following figure shows the four classes in this example and how they are related. Classes and Packages of the Example Used to Illustrate Access Levels The following table shows where the members of the Alpha class are visible for each of the access modifiers that can be applied to them. Visibility Modifier public protected no modifier private Alpha Y Y Y Y Beta Y Y Y N Alphasub Y Y N N Gamma Y N N N Tips on Choosing an Access Level: If other programmers use your class, you want to ensure that errors from misuse cannot happen. Access levels can help you do this. Use the most restrictive access level that makes sense for a particular member. Use private unless you have a good reason not to. Avoid public fields except for constants. (Many of the examples in the tutorial use public fields. This may help to illustrate some points concisely, but is not recommended for production code.) Public fields tend to link you to a particular implementation and limit your flexibility in changing your code. 9.10 Access Control and Inheritance: http://www.tutorialspoint.com/java/java_access_modifiers.htm The following rules for inherited methods are enforced: Methods declared public in a superclass also must be public in all subclasses. Methods declared protected in a superclass must either be protected or public in subclasses; they cannot be private. Methods declared without access control (no modifier was used) can be declared more private in subclasses. Methods declared private are not inherited at all, so there is no rule for them. 9.11 The instanceof operator Vedere §9.10 del libro Con riferimento al progetto network-v2 estendere la classe NewsFeed introducendo la classe ElaboratedNewsFeed che introduce il metodo showMessage che stampa solo i MessagePost e showPhoto che stampa solo i PhotoPost. 9.12 Per ripassare 10 Classi astratte e interfacce 1. Vedere le slide su classi astratte e interfacce 2. Vedere gli esempi tratti da “Java How to Program” 3. Svolgere il seguente esercizio: Esercizio: In riferimento al diagramma UML allegato costruire la seguente gerarchia di classi secondo la descrizione seguente: La prima classe descrive un lavoratore generico (Lavoratore). Nello specifico il lavoratore possiede la proprietà matricola, la proprietà cognome, la proprietà nome Inoltre, un lavoratore per essere tale deve implementare un metodo per il calcolo dello stipendio mensile. La seconda classe descrive un lavoratore a progetto (Contrattista). Nello specifico il lavoratore a progetto è un lavoratore, per cui possiede tutte le caratteristiche della classe Lavoratore e possiede la proprietà oreMensili, che indica il numero di ore lavorate e la proprietà euroOra, che indica la tariffa oraria del lavoratore a progetto. Inoltre, come richiesto dalla superclasse Lavoratore, la classe Contrattista implementa il metodo per il calcolo dello stipendio. La terza classe descrive un lavoratore a tempo indererminato (LavoratoreTempoIndeterminato).Nello specifico lavoratore a tempo indeterminato è un lavoratore, per cui possiede tutte le caratteristiche della classe Lavoratore e possiede la proprietà mensilita, che indica il numero di mensilità del lavoratore a tempo indeterminato,e la proprietà lordoAnnuo, che indica la cifra annua che il lavoratore a tempo indeterminato riceve. Inoltre,come richiesto dalla superclasse Lavoratore, la classe LavoratoreTempoIndeterminato implementa il metodo per il calcolo dello stipendio. La quarta classe descrive un direttore che si occupa dei pagamenti dei propri lavoratori(Direttore). Un direttore è un lavoratore a tempo indeterminato che in più percepisce un bonus mensile per il raggiungimento degli obiettivi aziendali. Dipendentemente dal tipo di lavoratore, il direttore calcola lo stipendio 11 Object as a Superclass Tratto da https://docs.oracle.com/javase/tutorial/java/IandI/objectclass.html The Object class, in the java.lang package, sits at the top of the class hierarchy tree. Every class is a descendant, direct or indirect, of the Object class. Every class you use or write inherits the instance methods of Object. You need not use any of these methods, but, if you choose to do so, you may need to override them with code that is specific to your class. The methods inherited from Object that are discussed in this section are: protected Object clone() throws CloneNotSupportedException Creates and returns a copy of this object. public boolean equals(Object obj) Indicates whether some other object is "equal to" this one. protected void finalize() throws Throwable Called by the garbage collector on an object when garbage collection determines that there are no more references to the object public final Class getClass() Returns the runtime class of an object. public int hashCode() Returns a hash code value for the object. public String toString() Returns a string representation of the object. 11.1 Clonazione di oggetti Clonare un oggetto vuol dire creare una “copia” di un oggetto http://www.dis.uniroma1.it/~liberato/laboratorio/clone/clone.html Non tutti gli oggetti sono clonabili Non tutti gli oggetti sono clonabili: String a, b; a=new String("abcd"); b=(String) a.clone(); // errore Le stringhe non si clonano in quanto sono oggetti immutabili. Per creare una stringa che sia la copia di un'altra basta usare il copy constructor: a=new String("abcd"); b=String(a); // crea una nuova istanza della stringa con gli stessi caratteri di a Per le stringhe si può fare la copia in modo più efficiente sfruttando il fatto che si tratta di oggetti immutabili: a=new String("abcd"); String b = a; In questo caso non c'è nessun problema a fare la copia passando il riferimento allo stesso oggetto dal momento che una volta creata una stringa non può più essere modificata. The clone() Method If a class, or one of its superclasses, implements the Cloneable interface, you can use the clone() method to create a copy from an existing object. To create a clone, you write: aCloneableObject.clone(); Object's implementation of this method checks to see whether the object on which clone() was invoked implements the Cloneable interface. If the object does not, the method throws a CloneNotSupportedException exception. Exception handling will be covered in a later lesson. For the moment, you need to know that clone() must be declared as protected Object clone() throws CloneNotSupportedException or: public Object clone() throws CloneNotSupportedException if you are going to write a clone() method to override the one in Object. If the object on which clone() was invoked does implement the Cloneable interface, Object's implementation of the clone() method creates an object of the same class as the original object and initializes the new object's member variables to have the same values as the original object's corresponding member variables. The simplest way to make your class cloneable is to add implements Cloneable to your class's declaration. Then your objects can invoke the clone() method. For some classes, the default behavior of Object's clone() method works just fine. If, however, an object contains a reference to an external object, say ObjExternal , you may need to override clone() to get correct behavior. Otherwise, a change in ObjExternal made by one object will be visible in its clone also. This means that the original object and its clone are not independent—to decouple them, you must override clone() so that it clones the object and ObjExternal. Then the original object references ObjExternal and the clone references a clone of ObjExternal, so that the object and its clone are truly independent. Copia superficiale: un primo esempio Clonazione usando clone di Object class Studente implements Cloneable { ... public Object clone() { try { return super.clone(); } catch(CloneNotSupportedException e) { return null; } } } la classe deve implementare Cloneable dato che clone di Object ha throws CloneNotSupportedException, va catturata Clonazione superficiale in classi derivate Se Borsista estende Studente: class Borsista extends Studente { ... public Object clone() { return super.clone(); } } Commenti: Non serve implements Cloneable dato che Studente implementa l'interfaccia Cloneable, anche Borsista la implementa automaticamente in modo indiretto Non serve catturare l'eccezione, poiché viene già fatto nel clone di Studente Chi clona l'oggetto? Quando si invoca clone di Borsista: viene invocato clone di Studente che invoca clone di Object È sempre clone di Object che fa la copia! Copia superficiale e profonda: differenze Se un oggetto contiene altri riferimenti a oggetti: Copia superficiale: viene copiato solo l'oggetto Copia profonda: viene copiato l'oggetto e tutti quelli collegati Esempio: Classe Segmento class Segmento { Point i, f; } Clonazione superficiale: Gli oggetti Point sono esattamente gli stessi. Clonazione profonda: Gli oggetti Point non sono gli stessi, ma hanno gli stessi valori dentro Prima della copia: Dopo la copia superficiale: Copia superficiale Clone di Object fa la copia del solo oggetto class Segmento implements Cloneable { Point i, f; public Object clone() { try { return super.clone(); } catch(CloneNotSupportedException e) { return null; } } } Copia profonda: significato Ha senso richiedere una copia anche dei due oggetti Point Questo non viene fatto da clone di Object, ma va fatto manualmente: Copia profonda: realizzazione Occorre invocare clone su ognuno degli oggetti di cui voglio fare la copia Notare che super.clone() ha tipo di ritorno Object Per poter accedere alle componenti, devo fare il cast Lo stesso vale per le componenti class Segmento implements Cloneable{ Point i, f; public Object clone() { try { Segmento s; s=(Segmento) super.clone(); s.i=(Point) this.i.clone(); s.f=(Point) this.f.clone(); return s; } catch(CloneNotSupportedException e) { return null; } } } Osservazioni: https://docs.oracle.com/javase/8/docs/api/java/lang/Cloneable.html https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone-- 11.2 Uguaglianza tra oggetti The equals() Method The equals() method compares two objects for equality and returns true if they are equal. The equals()method provided in the Object class uses the identity operator (==) to determine whether two objects are equal. For primitive data types, this gives the correct result. For objects, however, it does not. The equals() method provided by Object tests whether the object references are equal—that is, if the objects compared are the exact same object. To test whether two objects are equal in the sense of equivalency (containing the same information), you must override the equals() method. Here is an example of a Book class that overrides equals(): public class Book { ... public boolean equals(Object obj) { if (obj instanceof Book) return ISBN.equals((Book)obj.getISBN()); else return false; } } Consider this code that tests two instances of the Book class for equality: // Swing Tutorial, 2nd edition Book firstBook = new Book("0201914670"); Book secondBook = new Book("0201914670"); if (firstBook.equals(secondBook)) { System.out.println("objects are equal"); } else { System.out.println("objects are not equal"); } This program displays objects are equal even though firstBook and secondBook reference two distinct objects. They are considered equal because the objects compared contain the same ISBN number. You should always override the equals() method if the identity operator is not appropriate for your class. Note: If you override equals() , you must override hashCode() as well. Uguaglianza superficiale Si confrontano i riferimenti degli oggetti Due oggetti Segmento sono “equals” se i loro campi sono esattamente uguali (contengono riferimenti agli stessi identici oggetti) class Segmento { Point i, f; // equals superficiale public boolean equals(Object o) { if(o==null) return false; if(this.getClass()!=o.getClass()) return false; Segmento s=(Segmento) o; return((this.i==s.i)&&(this.f==s.f)); } } Uguaglianza profonda Gli oggetti vengono confrontati in base ai loro valori, non ai loro riferimenti: class Segmento { Point i, f; public boolean equals(Object o) { if(o==null) return false; if(this.getClass()!=o.getClass()) return false; Segmento s=(Segmento) o; if(this.i==null) { if(s.i!=null) return false; } else if(!this.i.equals(s.i)) return false; if(this.f==null) { if(s.f!=null) return false; } else if(!this.f.equals(s.f)) return false; return true; } } 11.3 The finalize() Method The Object class provides a callback method, finalize(), that may be invoked on an object when it becomes garbage. Object's implementation of finalize() does nothing—you can override finalize() to do cleanup, such as freeing resources. The finalize() method may be called automatically by the system, but when it is called, or even if it is called, is uncertain. Therefore, you should not rely on this method to do your cleanup for you. For example, if you don't close file descriptors in your code after performing I/O and you expect finalize() to close them for you, you may run out of file descriptors. 11.4 The getClass() Method You cannot override getClass . The getClass() method returns a Class object, which has methods you can use to get information about the class, such as its name (getSimpleName() ), its superclass (getSuperclass()), and the interfaces it implements (getInterfaces() ). For example, the following method gets and displays the class name of an object: void printClassName(Object obj) { System.out.println("The object's" + " class is " + obj.getClass().getSimpleName()); } 11.5 The hashCode() Method The value returned by hashCode() is the object's hash code, which is the object's memory address in hexadecimal. By definition, if two objects are equal, their hash code must also be equal. If you override the equals() method, you change the way two objects are equated and Object 's implementation of hashCode() is no longer valid. Therefore, if you override the equals() method, you must also override the hashCode() method as well. https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode-http://tutorials.jenkov.com/java-collections/hashcode-equals.html http://stackoverflow.com/questions/27581/what-issues-should-be-considered-when-overriding-equals-andhashcode-in-java 11.6 The toString() Method You should always consider overriding the toString() method in your classes. The Object's toString() method returns a String representation of the object, which is very useful for debugging. The String representation for an object depends entirely on the object, which is why you need to override toString() in your classes. You can use toString() along with System.out.println() to display a text representation of an object, such as an instance of Book: System.out.println(firstBook.toString()); which would, for a properly overridden toString() method, print something useful, like this: ISBN: 0201914670; The Swing Tutorial; A Guide to Constructing GUIs, 2nd Edition 11.7 Confronto tra oggetti http://www.mkyong.com/java/java-object-sorting-example-comparable-and-comparator/ http://docs.oracle.com/javase/tutorial/collections/interfaces/order.html Un approccio graduale...con l'uso delle interfacce java.lang.Comparable https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html per confrontare oggetti in base a una proprietà java.util.Comparator https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html per confrontare oggetti in base a un operatore (si usa quando si vuole confrontare gli oggetti sulla base di più proprietà) L'interfaccia java.lang.Comparable Supponiamo di avere la seguente classe: public class Fruit{ private String fruitName; private String fruitDesc; private int quantity; public Fruit(String fruitName, String fruitDesc, int quantity) { super(); this.fruitName = fruitName; this.fruitDesc = fruitDesc; this.quantity = quantity; } public String getFruitName() { return fruitName; } public void setFruitName(String fruitName) { this.fruitName = fruitName; } public String getFruitDesc() { return fruitDesc; } public void setFruitDesc(String fruitDesc) { this.fruitDesc = fruitDesc; } public int getQuantity() { return quantity; } public void setQuantity(int quantity) { this.quantity = quantity; } } Supponiamo di voler ordinare una collection di oggetti di tipo Fruit in base a una proprietà da noi scelta, ad esempio la quantità. Come facciamo? Occorre implementare l'interfaccia java.lang.Comparable e fare overriding del metodo compareTo() come descritto nel seguente esempio: public class Fruit implements Comparable<Fruit>{ private String fruitName; private String fruitDesc; private int quantity; public Fruit(String fruitName, String fruitDesc, int quantity) { super(); this.fruitName = fruitName; this.fruitDesc = fruitDesc; this.quantity = quantity; } public String getFruitName() { return fruitName; } public void setFruitName(String fruitName) { this.fruitName = fruitName; } public String getFruitDesc() { return fruitDesc; } public void setFruitDesc(String fruitDesc) { this.fruitDesc = fruitDesc; } public int getQuantity() { return quantity; } public void setQuantity(int quantity) { this.quantity = quantity; } @Override public int compareTo(Fruit compareFruit) { int compareQuantity = compareFruit.getQuantity(); //ascending order return this.quantity - compareQuantity; //descending order //return compareQuantity - this.quantity; } } Possiamo ora eseguire il seguente codice di esempio: import java.util.Arrays; public class SortFruitObject{ public static void main(String args[]){ Fruit[] fruits = new Fruit[4]; Fruit pineappale = new Fruit("Pineapple", "Pineapple description",70); Fruit apple = new Fruit("Apple", "Apple description",100); Fruit orange = new Fruit("Orange", "Orange description",80); Fruit banana = new Fruit("Banana", "Banana description",90); fruits[0]=pineappale; fruits[1]=apple; fruits[2]=orange; fruits[3]=banana; Arrays.sort(fruits); int i=0; for(Fruit temp: fruits){ System.out.println("fruits " + ++i + " : " + temp.getFruitName() + ", Quantity : " + temp.getQuantity()); } } } Ordinamento di Array con java.util.Arrays.sort() Nel precedente esempio si è fatto uso del metodo statico sort della classe java.util.Arrays per ordinare un array http://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html public class Arrays extends Object This class contains various methods for manipulating arrays (such as sorting and searching). This class also contains a static factory that allows arrays to be viewed as lists. The methods in this class all throw a NullPointerException, if the specified array reference is null, except where noted. Se l'array fruits fosse stato un array di int o di double non ci sarebbe stato alcun bisogno di implementare l'interfaccia Comparable. Bastava applicare il metodo statico Arrays.sort(). int a[]={30,7,9,20}; Arrays.sort(a); System.out.println(Arrays.toString(a)); Se l'array fruits fosse stato un array di String si poteva fare l'ordinamento alfabetico ascendente usando ancora il metodo Arrays.sort() String[] fruits = new String[] {"Pineapple","Apple", "Orange", "Banana"}; Arrays.sort(fruits); int i=0; for(String temp: fruits){ System.out.println("fruits " + ++i + " : " + temp); } Output fruits 1 : Apple fruits 2 : Banana fruits 3 : Orange fruits 4 : Pineapple Domanda: e se si volesse un ordinamento discendente? Opzione 1: ridefinire il metodo compareTo in modo da restituire l'opposto di quello che è stato definito nell'esempio precedente. Opzione 2: utilizzare la versione di Arrays.sort che ha come input l'array da ordinare e un comparatore. static sort(T[] a, Comparator<? super T> c) Sorts the specified array of objects according to the order induced by the specified comparator. L'istruzione per ordinare l'array in modo decrescente è in questo caso: <T> void Arrays.sort(fruits, Collections.reverseOrder()); //occore importare java.util.Collections I dettagli saranno chiariti nei prossimi paragrafi. Ordinamento di List con java.util.Collections La classe java.util.Collections https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html This class consists exclusively of static methods that operate on or return collections. It contains polymorphic algorithms that operate on collections, "wrappers", which return a new collection backed by a specified collection, and a few other odds and ends. The methods of this class all throw a NullPointerException if the collections or class objects provided to them are null. java.util.Collections contiene alcuni metodi per ordinare liste, come ad esempio gli ArrayList static sort(List<T> list) Sorts the specified list into ascending order, according to the natural ordering of its elements. static sort(List<T> list, Comparator<? <T> void super T> c) Sorts the specified list according to the order induced by the specified comparator. <T extends Comparable<? super T>> void static void reverse(List<?> list) Reverses the order of the elements in the specified list. Ad esempio List<Double> testList=new ArrayList(); testList.add(0.5); testList.add(0.2); testList.add(0.9); testList.add(0.1); testList.add(0.1); testList.add(0.54); testList.add(0.71); Si può ordinare in senso decrescente con le istruzioni: Collections.sort(testList); //ordina in senso ascendente Collections.reverse(testList); //inverte gli elementi Un ArrayList di oggetti, ad esempio di Fruit, si può ordinare con il metodo Collection.sort facendo in modo che la classe degli oggetti (Fruit) implementi l'interfaccia Comparable, come nel precedente esempio. In tal caso si potrà avere un esempio come il seguente: List<Fruit> fruits= new ArrayList<Fruit>(); Fruit fruit; for(int i=0;i<100;i++) { fruit = new fruit(); fruit.setname(...); fruits.add(fruit); } Per ordinare l'ArrayList basta ora fare Collections.sort(fruitList); Collections vs Collection Attenzione! In Java esiste anche l’interfaccia java.util.Collection (senza la “s” finale) che è completamente diversa da java.util.Collections. Infatti Collections è una classe contenente esclusivamente metodi statici di utilità per la manipolazione di oggetti di tipo Collection (o derivati). https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html Ordinamento mediante l'interfaccia java.util.Comparator Oltre a definire un metodo per effettuare l'ordinamento naturale rispetto a una proprietà con il metodo compareTo è possibile definire dei comparatori personalizzati che definiscono un ordinamento mediante condizioni complesse, come riportato nel seguente esempio: http://java67.blogspot.it/2012/10/how-to-sort-object-in-java-comparator-comparable-example.html package provacomparator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * * Java program to test Object sorting in Java. This Java program * test Comparable and Comparator implementation provided by Order * class by sorting list of Order object in ascending and descending order. * Both in natural order using Comparable and custom Order using Comparator in Java * * @author http://java67.blogspot.com */ public class ObjectSortingExample { public static void main(String args[]) { //Creating Order object to demonstrate Sorting of Object in Java Order ord1 = new Order(101,2000, "Sony"); Order ord2 = new Order(102,4000, "Hitachi"); Order ord3 = new Order(103,6000, "Philips"); //putting Objects into Collection to sort List<Order> orders = new ArrayList<Order>(); orders.add(ord3); orders.add(ord1); orders.add(ord2); //printing unsorted collection System.out.println("Unsorted Collection : " + orders); //Sorting Order Object on natural order – ascending //in questo caso si usa il metodo compareTo ridefinito Collections.sort(orders); //printing sorted collection System.out.println("List of Order object sorted in natural order : " + orders); // Sorting object in descending order in Java Collections.sort(orders, Collections.reverseOrder()); System.out.println("List of object sorted in descending order : " + orders); //Sorting object using Comparator in Java Collections.sort(orders, new Order.OrderByAmount()); System.out.println("List of Order object sorted using Comparator - amount : " + orders); // Comparator sorting Example - Sorting based on customer Collections.sort(orders, new Order.OrderByCustomer()); System.out.println("Collection of Orders sorted using Comparator - by customer : " + orders); } } /* * Order class is a domain object which implements * Comparable interface to provide sorting on natural order. * Order also provides couple of custom Comparators to * sort object based upon amount and customer */ class Order implements Comparable<Order> { private int orderId; private int amount; private String customer; /* * Comparator implementation to Sort Order object based on Amount */ public static class OrderByAmount implements Comparator<Order> { @Override public int compare(Order o1, Order o2) { return o1.amount > o2.amount ? 1 : (o1.amount < o2.amount ? -1 : 0); } } /* * Anohter implementation or Comparator interface to sort list of Order object * based upon customer name. */ public static class OrderByCustomer implements Comparator<Order> { @Override public int compare(Order o1, Order o2) { return o1.customer.compareTo(o2.customer); } } public Order(int orderId, int amount, String customer) { this.orderId = orderId; this.amount = amount; this.customer = customer; } public int getAmount() {return amount; } public void setAmount(int amount) {this.amount = amount;} public String getCustomer() {return customer;} public void setCustomer(String customer) {this.customer = customer;} public int getOrderId() {return orderId;} public void setOrderId(int orderId) {this.orderId = orderId;} /* * Sorting on orderId is natural sorting for Order. */ @Override public int compareTo(Order o) { return this.orderId > o.orderId ? 1 : (this.orderId < o.orderId ? -1 : 0); } /* * implementing toString method to print orderId of Order */ @Override public String toString(){ return String.valueOf(orderId); } } 11.8 Classi Nested Nell'esempio precedente la classe Order ha al suo interno due static nested class, OrderByCustomer e OrderByAmount, che rappresentano i comparatori. In Java è possibile dichiarare una classe all'interno di un'altra classe per alcuni usi specifici (ad esempio la creazione di comparatori) http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html The Java programming language allows you to define a class within another class. Such a class is called a nested class and is illustrated here: class OuterClass { ... class NestedClass { ... } } Terminology: Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are called static nested classes. Non-static nested classes are called inner classes. class OuterClass { ... static class StaticNestedClass { ... } class InnerClass { ... } } A nested class is a member of its enclosing class. Non-static nested classes (inner classes) have access to other members of the enclosing class, even if they are declared private. Static nested classes do not have access to other members of the enclosing class. As a member of the OuterClass, a nested class can be declared private, public, protected, or package private. (Recall that outer classes can only be declared public or package private.) Osservazioni L'interfaccia Comparator è molto potente in quanto permette di definire diversi criteri di confronto sulla stessa classe base. Questa interfaccia comprende due metodi astratti compare e equals: http://docs.oracle.com/javase/7/docs/api/java/util/Comparator.html Method Summary Methods Modifier and Type Method and Description compare(T o1, T o2) Compares its two arguments for order. int equals(Object obj) Indicates whether some other object is "equal to" this comparator. Si noti che una classe che implementa l'interfaccia Comparator deve implementare il metodo compare, ma non è tenuta a implementare il metodo equals dal momento che ogni classe eredita il metodo equals di Object e che, come riportato nella documentazione del metodo equals di Comparator, “ it is always safe not to override Object.equals(Object). ” boolean L'interfaccia Comparator trova la sua migliore espressione come classe nested in un'altra classe dal momento che in questo modo si mantiene la coesione del codice (cohesion): un comparatore ha senso in quanto definisce una relazione d'ordine su una classe base. Oltre che con l'ausilio di classi statiche nested, come mostrato nell'esempio della classe Order, è anche possibile utilizzare l'interfaccia Comparator nel seguente modo: http://www.mkyong.com/java/java-object-sorting-example-comparable-andcomparator/ import java.util.Comparator; public class Fruit implements Comparable<Fruit>{ private String fruitName; private String fruitDesc; private int quantity; public Fruit(String fruitName, String fruitDesc, int quantity) { super(); this.fruitName = fruitName; this.fruitDesc = fruitDesc; this.quantity = quantity; } public String getFruitName() { return fruitName; } public void setFruitName(String fruitName) { this.fruitName = fruitName; } public String getFruitDesc() { return fruitDesc; } public void setFruitDesc(String fruitDesc) { this.fruitDesc = fruitDesc; } public int getQuantity() { return quantity; } public void setQuantity(int quantity) { this.quantity = quantity; } public int compareTo(Fruit compareFruit) { int compareQuantity = ((Fruit) compareFruit).getQuantity(); //ascending order return this.quantity - compareQuantity; //descending order //return compareQuantity - this.quantity; } public static Comparator<Fruit> FruitNameComparator = new Comparator<Fruit>() { public int compare(Fruit fruit1, Fruit fruit2) { String fruitName1 = fruit1.getFruitName().toUpperCase(); String fruitName2 = fruit2.getFruitName().toUpperCase(); //ascending order return fruitName1.compareTo(fruitName2); //descending order //return fruitName2.compareTo(fruitName1); } }; } Nell'esempio precedente la classe Fruit, oltre a implementare l'interfaccia Comparable per definire l'ordinamento naturale sugli oggetti della classe Fruit (tramite il metodo compareTo), introduce anche un campo statico di tipo public static Comparator<Fruit> inizializzato con il riferimento a un oggetto di una classe anonima che implementa l'interfaccia Comparator<Fruit>, infatti: FruitNameComparator è inizializzato con il riferimento all'oggetto creato da new Comparator<Fruit>(){//classe anonima che implementa Comarator<Fruit> e ridefinisce il metodo compare} Con la versione precedente della classe Fruit è possibile ordinare un array di Fruit nel seguente modo; Arrays.sort(fruits, Fruit.FruitNameComparator); Oppure un ArrayList di Fruit: Collections.sort(listOfFruit, Fruit.FruitNameComparator); É anche possibile implementare l'interfaccia Comparator in classi distinte dalla classe base, come mostrato nel seguente esempio: http://javahungry.blogspot.com/2013/08/difference-between-comparable-and.html //Country.java public class Country{ int countryId; String countryName; public Country(int countryId, String countryName) { super(); this.countryId = countryId; this.countryName = countryName; } public int getCountryId() { return countryId; } public void setCountryId(int countryId) { this.countryId = countryId; } public String getCountryName() { return countryName; } public void setCountryName(String countryName) { this.countryName = countryName; } } //CountrySortbyIdComparator.java import java.util.Comparator; //If country1.getCountryId() < country2.getCountryId():then compare method will return -1 //If country1.getCountryId() > country2.getCountryId():then compare method will return 1 //If country1.getCountryId()==country2.getCountryId():then compare method will return 0 public class CountrySortByIdComparator implements Comparator<Country>{ @Override public int compare(Country country1, Country country2) { return (country1.getCountryId() < country2.getCountryId() ) ? -1: (country1.getCountryId() > country2.getCountryId() ) ? 1:0 ; } } //ComparatorMain.java import import import import java.util.ArrayList; java.util.Collections; java.util.Comparator; java.util.List; public class ComparatorMain { public static void main(String[] args) { Country indiaCountry=new Country(1, "India"); Country chinaCountry=new Country(3, "USA"); Country nepalCountry=new Country(4, "Russia"); Country bhutanCountry=new Country(2, "Japan"); List<Country> listOfCountries = new ArrayList<Country>(); listOfCountries.add(indiaCountry); listOfCountries.add(usaCountry); listOfCountries.add(russiaCountry); listOfCountries.add(japanCountry); System.out.println("Before Sort by id : "); for (int i = 0; i < listOfCountries.size(); i++) { Country country=(Country) listOfCountries.get(i); System.out.println("Country Id: "+country.getCountryId()+"||"+"Country name: "+country.getCountryName()); } Collections.sort(listOfCountries,new CountrySortByIdComparator()); System.out.println("After Sort by id: "); for (int i = 0; i < listOfCountries.size(); i++) { Country country=(Country) listOfCountries.get(i); System.out.println("Country Id: "+country.getCountryId()+"|| "+"Country name: "+country.getCountryName()); } //Sort by countryName – utilizzo di un oggetto di una classe anonima Collections.sort(listOfCountries,new Comparator<Country>() { @Override public int compare(Country o1, Country o2) { return o1.getCountryName().compareTo(o2.getCountryName()); } }); System.out.println("After Sort by name: "); for (int i = 0; i < listOfCountries.size(); i++) { Country country=(Country) listOfCountries.get(i); System.out.println("Country Id: "+country.getCountryId()+"|| "+"Country name: "+country.getCountryName()); } } } Talvolta è utile effettuare la conversione tra ArrayList e Array e viceversa 11.9 Convertire un ArrayList in un Array ArrayList class has a method called toArray() that we are using in our example to convert it into Arrays. Following is simple code snippet that converts an array list of countries into string array. List<String> list = new ArrayList<String>(); list.add("India"); list.add("Switzerland"); list.add("Italy"); list.add("France"); String [] countries = list.toArray(new String[list.size()]); So to convert ArrayList of any class into array use following code. Convert T into the class whose arrays you want to create. List<T> list = new ArrayList<T>(); T [] countries = list.toArray(new T[list.size()]); 11.10 Convertire ArrayList in Array Vediamo il seguente esempio: http://viralpatel.net/blogs/convert-arraylist-to-arrays-in-java/ import java.util.ArrayList; import java.util.List; import java.util.Arrays; String[] countries = {"India", "Switzerland", "Italy", "France"}; List list = Arrays.asList(countries); System.out.println("ArrayList of Countries:" + list); The above code will work great. But list object is immutable. Thus you will not be able to add new values to it. In case you try to add new value to list, it will throw UnsupportedOperationException. Related: Resolve UnsupportedOperationException exception Thus simply create a new List from that object. See below: String[] countries = {"India", "Switzerland", "Italy", "France"}; List list = new ArrayList(Arrays.asList(countries)); System.out.println("ArrayList of Countries:" + list); 12 Interfacce Grafiche (GUI) 12.1 Elementi di un'interfaccia grafica Per gestire un'interfaccia grafica dobbiamo essere in grado di controllare tre elementi: ■ What kinds of elements can we show on screen? ■ How do we arrange those elements? ■ How do we react to user input? Un'interfaccia grafica è composta di tre cose: Components are the individual parts that a GUI is built from. They are things such as buttons, menus, menu items, checkboxes, sliders, text fields, and so on. Layout deals with the issue of how to arrange the components on screen. Event handling refers to the technique we shall use to deal with user input. 12.2 AWT e Swing Java has two GUI libraries. The older one is called AWT (Abstract Window Toolkit) and was introduced as part of the original Java API. Later, a much-improved GUI library, called Swing, was added to Java. Swing makes use of some of the AWT classes, replaces some AWT classes with its own versions, and adds many new classes (Figure 11.1). Wherever there are equivalent classes in AWT and Swing, the Swing versions have been identified by adding the letter J to the start of the class name. You will, for example, see classes named Button and JButton, Frame and Jframe, Menu and JMenu, and so on. The classes starting with a J are the Swing versions; these are the ones we shall use, and the two should not be mixed in an application. 12.3 La prima applicazione grafica Consideriamo l'esempio imageviewer0-1 import java.awt.*; import java.awt.event.*; import javax.swing.*; public class ImageViewer { private JFrame frame; public ImageViewer() { makeFrame(); } private void makeFrame() { frame = new JFrame("ImageViewer mio"); Container contentPane = frame.getContentPane(); //JLabel label = new JLabel("I am a label. I can display some text."); JButton button1 = new JButton("premi qui!"); //JButton button2 = new JButton("premi li!"); //contentPane.add(label); contentPane.add(button1); //contentPane.add(button2); frame.pack(); frame.setVisible(true); } } In maniera alternativa si può definire ImageViewer come sottoclasse di Jframe import java.awt.*; import java.awt.event.*; import javax.swing.*; public class ImageViewer extends JFrame { public ImageViewer() { super("ImageViewer"); makeFrame(); setVisible(true); } private void makeFrame() { Container contentPane = getContentPane(); JLabel label = new JLabel("I am a label. I can display some text."); contentPane.add(label); pack(); } } Si noti che il punto di partenza per la costruzione di un oggetto grafico è un oggetto di tipo JFrame. All'oggetto di tipo JFrame è poi collegato un oggetto di tipo Container mediante il metodo getContentPane dell'oggetto JFrame. Sull'oggetto Container sono aggiunti altri elementi grafici come JLabel e JButton mediante il metodo add dell'oggetto Container. La costruzione della finestra è quindi terminata invocando il metodo pack che permette di definirne le dimensioni in base al suo contenuto. Aggiunta di menu First, we create the menus. Three classes are involved: JMenuBar —An object of this class represents a menu bar that can be displayed below the title bar at the top of a window. Every window has at most one JMenuBar. JMenu —Objects of this class represent a single menu (such as the common File, Edit, or Help menus). Menus are often held in a menu bar. They could also appear as pop-up menus, but we shall not do that now. JMenuItem —Objects of this class represent a single menu item inside a menu (such as Open or Save). The class JFrame has a method called setJMenuBar. We can create a menu bar and use this method to attach our menu bar to the frame: JMenuBar menubar = new JMenuBar(); frame.setJMenuBar(menubar); Now we are ready to create a menu and add it to the menu bar: JMenu fileMenu = new JMenu("File"); menubar.add(fileMenu); These two lines create a menu labeled File and insert it into our menu bar. Finally, we can add menu items to the menu. The following lines add two items, labeled Open and Quit, to the File menu: JMenuItem openItem = new JMenuItem("Open"); fileMenu.add(openItem); JMenuItem quitItem = new JMenuItem("Quit"); fileMenu.add(quitItem); Gestione degli eventi Swing uses a very flexible model to deal with GUI input: an event-handling model with event listeners. The Swing framework itself and some of its components raise events when something happens that other objects may be interested in. There are different types of events caused by different types of actions. When a button is clicked or a menu item is selected, the component raises an ActionEvent. When a mouse is clicked or moved, a MouseEvent is raised. When a frame is closed or iconified, a WindowEvent is generated. There are many other types of events. Any of our objects can become an event listener for any of these events. When it is a listener, it will get notified about any of the events it listens to. An object becomes an event listener by implementing one of several existing listener interfaces. If it implements the right interface, it can register itself with a component it wants to listen to. Let us look at an example. A menu item (class JMenuItem) raises an ActionEvent when it is activated by a user. Objects that want to listen to these events must implement the ActionListener interface from the java.awt.event package. Un esempio completo di menu e gestione degli eventi (con un solo listener) //progetto: imageviewer0-2 import java.awt.*; import java.awt.event.*; import javax.swing.*; public class ImageViewer implements ActionListener { private JFrame frame; public ImageViewer() { makeFrame(); } /** * Receive notification of an action. * @param event Details of the action. */ public void actionPerformed(ActionEvent event) { System.out.println("Menu item: " + event.getActionCommand()); } private void makeFrame() { frame = new JFrame("ImageViewer"); makeMenuBar(frame); Container contentPane = frame.getContentPane(); JLabel label = new JLabel("I am a label. I can display some text."); contentPane.add(label); // building is done - arrange the components and show frame.pack(); frame.setVisible(true); } /** * Create the main frame's menu bar. * @param frame The frame that the menu bar should be added to. */ private void makeMenuBar(JFrame frame) { JMenuBar menubar = new JMenuBar(); frame.setJMenuBar(menubar); // create the File menu JMenu fileMenu = new JMenu("File"); menubar.add(fileMenu); JMenuItem openItem = new JMenuItem("Open"); openItem.addActionListener(this); //registro l'oggetto corrente ImageViewer come listener di openItem fileMenu.add(openItem); JMenuItem quitItem = new JMenuItem("Quit"); quitItem.addActionListener(this); //registro l'oggetto corrente ImageViewer come listener di quitItem fileMenu.add(quitItem); } } Osservazione importante: meglio avere listeners distinti per oggetti distinti Si noti che con questo approccio l'oggetto ImageViewer è listener sia di openItem che di quitItem con un solo metodo actionPerformed. Se si volesse realizzare una funzionalità diversa per openItem e per quitItem al click del mouse dovremmo distinguere il tipo di evento con istruzioni del tipo: if(event.getActionCommand().equals("Open")) ... questo approccio ha diversi problemi: se cambia il nome del componente da “Open” a “Apri” bisogna modificare il codice. C'è un unico metodo centralizzato che ascolta tutti gli eventi - ogni volta che si aggiunge un componente bisogna modificare il metodo actionPerformed di ImageViewer La gestione degli eventi sulle interfacce grafiche risulta ottimizzata facendo ricorso al concetto di inner class già visto quando si è utilizzata l'interfaccia Comparator (in quel caso si trattava di static nested class). Inner classes (rivisitate) To solve the problems with centralized event dispatch mentioned above, we use a new construct that we have not discussed before: inner classes. Inner classes are classes that are declared textually inside another class: class EnclosingClass { ... class InnerClass { ... } } Instances of the inner class are attached to instances of the enclosing class; they can only exist together with an enclosing instance, and they exist conceptually inside the enclosing instance. One interesting detail is that statements in methods of the inner class can see and access private fields and methods of the enclosing class. There is obviously a very tight coupling between the two, therefore. The inner class is considered to be a part of the enclosing class just as are any of the enclosing class’s methods. We can now use this construct to make a separate action-listener class for every menu item we like to listen to. As they are separate classes, they can each have a separate actionPerformed method so that each of these methods handles only a single item’s activation. The structure is this: class ImageViewer { ... class OpenActionListener implements ActionListener { public void actionPerformed(ActionEvent event) { // perform open action } } class QuitActionListener implements ActionListener { public void actionPerformed(ActionEvent event) { // perform quit action } } } Once we have done this, we can now create instances of these inner classes in exactly the same way as for those of any other class. Note also that ImageViewer does not implement ActionListener anymore (we remove its actionPerformed method), but the two inner classes do. This now allows us to use instances of the inner classes as action listeners for the menu items. JMenuItem openItem = new JMenuItem("Open"); openItem.addActionListener(new OpenActionListener()); ... JMenuItem quitItem = new JMenuItem("Quit"); quitItem.addActionListener(new QuitActionListener()); In summary, instead of having the image-viewer object listen to all action events, we create separate listener objects for each possible event, where each listener object listens to one single event type. As every listener has its own actionPerformed method, we can now write specific handling code in these methods. Also, because the listener classes are in the scope of the enclosing class (they can access the enclosing class’s private fields and methods), they can make full use of the enclosing class in the implementation of the actionPerformed methods. Notice a couple of characteristics of these listener objects: We don’t bother storing them in variables—so, in effect, they are anonymous objects. Only the menu items have a reference to the listener objects, so that they can call their actionPerformed methods. We only create a single object from each of the inner classes, because each is highly specialized for a particular menu item. These characteristics will lead us to explore a further feature of Java in the next section. Anonymous inner classes Il progetto image-viewer0-3 implementa il concetto di inner class anonime per la gestione degli eventi generati dal click del mouse su un oggetto di tipo JMenuItem import java.awt.*; import java.awt.event.*; import javax.swing.*; public class ImageViewer { private JFrame frame; public ImageViewer() { makeFrame(); } // ---- implementation of menu functions ---- /** * Open function: open a file chooser to select a new image file. */ private void openFile() { // this is some test output, until we do this properly System.out.println("open file"); } /** * Quit function: quit the application. */ private void quit() { System.exit(0); } private void makeFrame() { frame = new JFrame("ImageViewer"); makeMenuBar(frame); Container contentPane = frame.getContentPane(); JLabel label = new JLabel("I am a label. I can display some text."); contentPane.add(label); // building is done - arrange the components and show frame.pack(); frame.setVisible(true); } /** * Create the main frame's menu bar. * @param frame The frame that the menu bar should be added to. */ private void makeMenuBar(JFrame frame) { JMenuBar menubar = new JMenuBar(); frame.setJMenuBar(menubar); // create the File menu JMenu fileMenu = new JMenu("File"); menubar.add(fileMenu); JMenuItem openItem = new JMenuItem("Open"); openItem.addActionListener(new ActionListener() {//qui inizia la classe anonima public void actionPerformed(ActionEvent e) { openFile(); } }); fileMenu.add(openItem); JMenuItem quitItem = new JMenuItem("Quit"); quitItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { quit(); } }); fileMenu.add(quitItem); } } When using an anonymous inner class, we create an inner class without naming it and immediately create a single instance of the class. In the action-listener code above, this is done with the code fragment new ActionListener() { public void actionPerformed(ActionEvent e) { openFile(); } } An anonymous inner class is created by naming a supertype (often an abstract class or an interface— here ActionListener), followed by a block that contains an implementation for its abstract methods. This looks unusual, because it is not otherwise permitted to create an instance of an abstract class or interface directly. In this example, we create a new subtype of ActionListener that implements the actionPerformed method. This new class does not receive a name. Instead, we precede it with the new keyword to create a single instance of this class. In our example, this single instance is an actionlistener object (it is of a subtype of ActionListener). It can be passed to a menu item’s addActionListener method and will invoke the openFile method of its enclosing class when activated. Each subtype of ActionListener created in this way represents a unique anonymous class. Just like named inner classes, anonymous inner classes are able to access the fields and methods of their enclosing classes. In addition, because they are defined inside a method, they are able to access the local variables and parameters of that method. However, an important rule is that local variables accessed in this way must be declared as final variables. 12.4 Un programma per la visualizzazione e modifica delle immagini: prima versione Apriamo il progetto imageviewer0-4: ImageFileManager is a helper class that provides static methods to read an image file (in JPEG or PNG format) from disk and return it in OFImage format and then save the OFImage back to disk. ImagePanel is a custom Swing component to show the image in our GUI. The OFImage class is our own custom format for representing an image in memory. You can think of an OFImage as a two-dimensional array of pixels. Each of the pixels can have a color. We use the standard class Color (from package java.awt) to represent each pixel’s color. OFImage is implemented as a subclass of the Java standard class BufferedImage (from package java.awt.image). BufferedImage gives us most of the functionality we want (it also represents an image as a two-dimensional array), but it does not have methods to set or get a pixel using a Color object (it uses different formats for this, which we do not want to use). So we made our own subclass that adds these two methods. import java.awt.*; import java.awt.event.*; import javax.swing.*; public class ImageViewer { private JFrame frame; private ImagePanel imagePanel; /** * Create an ImageViewer show it on screen. */ public ImageViewer() { makeFrame(); } // ---- implementation of menu functions ---- /** * Open function: open a file chooser to select a new image file. */ private void openFile() { //carico il file contenente l'immagine OFImage image = ImageFileManager.getImage(); //imposto l'immagine sul image panel imagePanel.setImage(image); frame.pack(); } /** * Quit function: quit the application. */ private void quit() { System.exit(0); } // ---- swing stuff to build the frame and all its components ---- /** * Create the Swing frame and its content. */ private void makeFrame() { //creo il frame frame = new Jframe("ImageViewer"); //creo la menubar makeMenuBar(frame); //creo il container Container contentPane = frame.getContentPane(); //creo un image panel imagePanel = new ImagePanel(); //aggiungo l'image panel al container contentPane.add(imagePanel); // building is done - arrange the components and show frame.pack(); frame.setVisible(true); } /** * Create the main frame's menu bar. * @param frame The frame that the menu bar should be added to. */ private void makeMenuBar(JFrame frame) { final int SHORTCUT_MASK = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); JMenuBar menubar = new JMenuBar(); frame.setJMenuBar(menubar); // create the File manu JMenu fileMenu = new JMenu("File"); menubar.add(fileMenu); JMenuItem openItem = new JMenuItem("Open"); openItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, SHORTCUT_MASK)); openItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { openFile(); } }); fileMenu.add(openItem); JMenuItem quitItem = new JMenuItem("Quit"); quitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, SHORTCUT_MASK)); quitItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { quit(); } }); fileMenu.add(quitItem); } } Nota: nell'esempio si fa uso di della classe KeyEvent e Toolkit per creare degli shortcut sui menù. In questa fase iniziale si può trascurare questi dettagli o anche rimuovere il relativo codice dall'esempio per non renderlo inutilmente pesante. 12.5 Layouts Supponiamo di voler inserire una etichetta (label) per indicare il nome del file aperto sopra l'immagine visualizzata e un'altra etichetta sotto per indicare alcune notifiche relative alle operazioni fatte sull'immagine , nel progetto imageviewer0-4. Si potrebbe pensare (erroneamente) di aggiungere il codice seguente al metodo makeFrame: private void makeFrame() { frame = new JFrame("ImageViewer"); makeMenuBar(frame); Container contentPane = frame.getContentPane(); imagePanel = new ImagePanel(); contentPane.add(imagePanel); JLabel filenameLabel = new JLabel("Nome file aperto"); contentPane.add(filenameLabel); JLabel statusLabel = new JLabel("Version 1.0"); contentPane.add(statusLabel); // building is done - arrange the components and show frame.pack(); frame.setVisible(true); } In questo caso il visualizzatore di immagini non mostra più l'immagine, ma soltanto l'etichetta “Version 1.0”. Se si prova a cambiare l'ordine di inserimento degli oggetti nel Container si otterrà di volta in volta un risultato diverso: l'applicazione visualizza sempre l'ultimo oggetto inserito. Il motivo di ciò sta nel fatto che non abbiamo definito un layout, vale a dire una disposizione, per la visualizzazione degli oggetti. Swing uses layout managers to arrange the layout of components in a GUI. Each container that holds components, such as the content pane, has an associated layout manager that takes care of arranging the components within that container. Swing provides several different layout managers to support different layout preferences. The most important are: FlowLayout, BorderLayout, GridLayout, and BoxLayout. Each of those is represented by a Java class in the Swing library, and each lays out in different ways the components under its control. The key differences between them are the ways in which they position components and how the available space is distributed between the components. You can find the examples illustrated here in the layouts project. Aprire il progetto layouts: BorderLayout private void makeFrame() { frame = new JFrame("BorderLayout Example"); Container contentPane = frame.getContentPane(); contentPane.setLayout(new BorderLayout()); contentPane.add(new JButton("north"), BorderLayout.NORTH); contentPane.add(new JButton("south"), BorderLayout.SOUTH); contentPane.add(new JButton("center"), BorderLayout.CENTER); contentPane.add(new JButton("west"), BorderLayout.WEST); contentPane.add(new JButton("east"), BorderLayout.EAST); frame.pack(); frame.setVisible(true); } FlowLayout private void makeFrame() { frame = new JFrame("FlowLayout Example"); Container contentPane = frame.getContentPane(); contentPane.setLayout(new FlowLayout()); contentPane.add(new JButton("first")); contentPane.add(new JButton("second")); contentPane.add(new JButton("the third string is very long")); contentPane.add(new JButton("fourth")); contentPane.add(new JButton("fifth")); frame.pack(); frame.setVisible(true); } GridLayout private void makeFrame() { frame = new JFrame("GridLayout Example"); Container contentPane = frame.getContentPane(); contentPane.setLayout(new GridLayout(3, 2)); contentPane.add(new JButton("first")); contentPane.add(new JButton("second")); contentPane.add(new JButton("the third string is very long")); contentPane.add(new JButton("fourth")); contentPane.add(new JButton("fifth")); frame.pack(); frame.setVisible(true); } BoxLayout private void makeFrame() { frame = new JFrame("BoxLayout Example"); Container contentPane = frame.getContentPane(); contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); contentPane.add(new JButton("first")); contentPane.add(new JButton("second")); contentPane.add(new JButton("the third string is very long")); contentPane.add(new JButton("fourth")); contentPane.add(new JButton("fifth")); frame.pack(); frame.setVisible(true); } Nested Containers Layouts can be nested. Many of the Swing components are containers. Containers appear to the outside as a single component, but they can contain multiple other components. Each container has its own layout manager attached. The most-used container is the class JPanel. A JPanel can be inserted as a component into the frame’s content pane, and then more components can be laid out inside the JPanel. fig., for example, shows an interface arrangement similar to that of the BlueJ main window. The content pane of this frame uses a BorderLayout, where the EAST position is unused. The NORTH area of this BorderLayout contains a Jpanel with a horizontal FlowLayout that arranges its components (say toolbar buttons) in a row. The SOUTH area is similar: another Jpanel with a FlowLayout. The button group in the WEST area was first placed into a JPanel with a one-column GridLayout to give all buttons the same width. This JPanel was then placed into another JPanel with a FlowLayout so that the grid did not extend over the full height of the WEST area. The outer JPanel is then inserted into the WEST area of the frame. Note how the container and the layout manager cooperate in the layout of the components. The container holds the components, but the layout manager decides their exact arrangement on screen. Every container has a layout manager. It will use a default layout manager if we do not explicitly set one. The default is different for different containers: the content pane of a JFrame, for example, has by default a BorderLayout, whereas JPanels use fig. 1: Layout innestati mediante Jpanel a FlowLayout by default. 12.6 Creazione di un'interfaccia grafica con NetBeans Un esempio guida: creare un convertitore di temperatura con NetBeans: http://docs.oracle.com/javase/tutorial/uiswing/learn/index.html Si veda anche il tutorial: https://netbeans.org/kb/docs/java/quickstart-gui.html Molti esempi e tutorial sulla creazione di interfacce grafiche con NetBeans si trovano a partire da questo link: https://netbeans.org/kb/trails/matisse.html in particolare: https://netbeans.org/kb/docs/java/gui-functionality.html https://netbeans.org/kb/docs/java/gui-filechooser.html https://netbeans.org/kb/docs/java/gui-image-display.html altri esempi qui: (swing tutorial): http://docs.oracle.com/javase/tutorial/uiswing/index.html Progetti da svolgere (riporgettare) con NetBeans (matisse): Molto utili gli esempi riportati nei Java tutotial ufficiali di Oracle: “Using Swing Components: Examples” http://docs.oracle.com/javase/tutorial/uiswing/examples/components/index.html#IconDemo Esercizio 1: Il “gioco del 15”. Versione con numeri Versione con immagini “puzzlefy” Per quest'ultima versione si considerino i seguenti link: http://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferedImage.html http://stackoverflow.com/questions/9417356/bufferedimage-resize http://www.mkyong.com/java/how-to-resize-an-image-in-java/ http://stackoverflow.com/questions/5813900/how-to-get-sub-image-from-buffered-image e il seguente esempio (IconDemoProject) tratto dai java tutorials: http://docs.oracle.com/javase/tutorial/uiswing/components/icon.html il cui codice si trova qui: http://docs.oracle.com/javase/tutorial/uiswing/examples/zipfiles/componentsCustomIconDemoProject.zip Esercizio 2: Emulare la calcolatrice standard di Windows Partire dal progetto my-calculator fornisce una classe per una calcolatrice (CalcEngine) che effettua solo le operazioni “+” e “-”. Creare un progetto NetBeans chiamato MyCalc con un form (UserInterface) e la classe CalcEngine. La classe CalcEngine ha il seguente diagramma UML: //file: UserInterface.java package mycalc; public class UserInterface extends javax.swing.JFrame { private CalcEngine calc; public UserInterface(CalcEngine calc) { this.calc = calc; initComponents(); } @SuppressWarnings("unchecked") //qui il “genereted code” di NetBeans }//fine del Jframe form //--------------------------------------------------------------------------//file MyCalcStarter.java package mycalc; public class MyCalcStarter { public static void main(String args[]) { /* Set the Nimbus look and feel */ CalcEngine calculator = new CalcEngine(); /* Create and display the form */ java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new UserInterface(calculator).setVisible(true); } }); } } A partire dal progetto MyCalc progettare l'interfaccia grafica della calcolatrice usando il tool Matisse di NetBeans. 13 Gestione delle eccezioni (rivisitate) Seguire il capitolo 11 del libro Java Haw to program – Exception Handling: a deeper look con gli esempi presenti sul sito del libro. 13.1 Approfondimenti http://howtodoinjava.com/2013/04/04/java-exception-handling-best-practices/ 13.2 Logging delle eccezioni e di situazioni specifiche: java.util.logging package http://tutorials.jenkov.com/java-logging/index.html 14 Gestione dei file in Java Seguire il capitolo 17 del libro Java Haw to program – Files, Streams and Object serialization con gli esempi riportati sul sito del libro. 14.1 java.io.File //gestione dei file e delle directory boolean canRead() Returns true if a file is readable by the current application; false otherwise. boolean canWrite() Returns true if a file is writable by the current application; false otherwise. boolean exists() Returns true if the file or directory represented by the File object exists; false otherwise. boolean isFile() Returns true if the name specified as the argument to the File constructor is a file; false otherwise. boolean isDirectory() Returns true if the name specified as the argument to the File constructor is a directory; false otherwise. boolean isAbsolute() Returns true if the arguments specified to the File constructor indicate an absolute path to a file or directory; false otherwise. String getAbsolutePath() Returns a String with the absolute path of the file or directory. String getName() Returns a String with the name of the file or directory. String getPath() Returns a String with the path of the file or directory. String getParent() Returns a String with the parent directory of the file or directory (i.e., the directory in which the file or directory is located). long length() Returns the length of the file, in bytes. If the File object represents a directory, an unspecified value is returned. long lastModified() Returns a platform-dependent representation of the time at which the file or directory was last modified. The value returned is useful only for comparison with other values returned by this method. String[] list() Returns an array of Strings representing a directory’s contents. Returns null if the File object does not represent a directory. Una panoramica sulle classi Java per la gestione dei file si trova qui: http://docs.oracle.com/javase/tutorial/essential/io/index.html e qui: http://tutorials.jenkov.com/java-io/index.html Esempi sui file di testo qui: http://www.javapractices.com/topic/TopicAction.do?Id=42 Esempi sui file binari qui: http://www.javapractices.com/topic/TopicAction.do?Id=245 14.2 java.util.Formatter e java.util.Scanner; Con Formatter il file di testo precedentemente creato (se esiste) è completamente sovrascritto. Con Scanner il delimitatore di default è un qualunque whitespace. import java.io.FileNotFoundException; import java.lang.SecurityException; import java.util.Formatter; import java.util.FormatterClosedException; import java.util.NoSuchElementException; import java.util.Scanner; public class CreateTextFile { private Formatter output; // object used to output text to file // enable user to open file public void openFile() { try { output = new Formatter( "clients.txt" ); // open the file } // end try catch ( SecurityException securityException ) { System.err.println( "You do not have write access to this file." ); System.exit( 1 ); // terminate the program } // end catch catch ( FileNotFoundException fileNotFoundException ) { System.err.println( "Error opening or creating file." ); System.exit( 1 ); // terminate the program } // end catch } // end method openFile // add records to file public void addRecords() { // object to be written to file AccountRecord record = new AccountRecord(); Scanner input = new Scanner( System.in ); System.out.printf( "%s\n%s\n%s\n%s\n\n", "To terminate input, type the end-of-file indicator ", "when you are prompted to enter input.", "On UNIX/Linux/Mac OS X type <ctrl> d then press Enter", "On Windows type <ctrl> z then press Enter" ); System.out.printf( "%s\n%s", "Enter account number (> 0), first name, last name and balance.", "? " ); //int contatore = 0; //int limit = 2; while ( input.hasNext() ) // loop until end-of-file indicator { try // output values to file { // retrieve data to be output record.setAccount( input.nextInt() ); // read account number record.setFirstName( input.next() ); // read first name record.setLastName( input.next() ); // read last name record.setBalance( input.nextDouble() ); // read balance if ( record.getAccount() > 0 ) { // write new record output.format( "%d %s %s %.2f\n", record.getAccount(), record.getFirstName(), record.getLastName(), record.getBalance() ); } // end if else { System.out.println( "Account number must be greater than 0." ); } // end else } // end try catch ( FormatterClosedException formatterClosedException ) { System.err.println( "Error writing to file." ); return; } // end catch catch ( NoSuchElementException elementException ) { System.err.println( "Invalid input. Please try again." ); input.nextLine(); // discard input so user can try again } // end catch System.out.printf( "%s %s\n%s", "Enter account number (>0),", "first name, last name and balance.", "? " ); //contatore++; } // end while } // end method addRecords // close file public void closeFile() { if ( output != null ) output.close(); } // end method closeFile } // end class CreateTextFile 14.3 Scrivere su file di testo con PrintWriter e BufferedWriter una riga alla volta Per scrivere una riga alla volta su file di testo: Classe java.io.PrintWriter oppure Classe java.io.BufferedWriter Quali differenze tra PrintWriter e BurreredWriter? http://stackoverflow.com/questions/1747040/difference-between-java-io-printwriter-and-java-iobufferedwriter // A.class File file = new File("blah.txt"); FileWriter fileWriter = new FileWriter(file); PrintWriter printWriter = new PrintWriter(fileWriter); // B.class File file = new File("blah.txt"); FileWriter fileWriter = new FileWriter(file); BufferedWriter bWriter = new BufferedWriter(fileWriter); What is the difference between these two methods? When should we use PrintWriter over BufferedWriter? The main reason to use the PrintWriter is to get access to the printXXX methods (like println(int)). You can essentially use a PrintWriter to write to a file just like you would use System.out to write to the console. A BufferedWriter is an efficient way to write to a file (or anything else) as it will buffer the characters in Java memory before (probably, depending on the implementation) dropping to C to do the writing to the file. There is no such concept as a "PrintReader" the closest you will come is probably java.util.Scanner. Nota: per aprire un file di testo in scrittura in modalità append conviene utilizzare la classe FileWriter http://docs.oracle.com/javase/7/docs/api/java/io/FileWriter.html usando il costruttore: FileWriter(File file, boolean append) http://stackoverflow.com/questions/1625234/how-to-append-text-to-an-existing-file-in-java PrintWriter out = null; try { out = new PrintWriter(new BufferedWriter(new FileWriter("writePath", true))); out.println("the text"); }catch (IOException e) { System.err.println(e); }finally{ if(out != null){ out.close(); } } Also, as of Java 7, you can use a try-with-resources statement . No finally block is required for closing the declared resource(s) because it is handled automatically, and is also less verbose: try(PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("writePath", true)))) { out.println("the text"); }catch (IOException e) { System.err.println(e); } 14.4 Leggere da file di testo una riga alla volta con BufferedReader Per leggere un file di testo conviene utilizzare le classi: FileReader e BufferedReader nel seguente modo: BufferedReader input = new BufferedReader(new FileReader(aFile)); 14.5 Un esempio di lettura da file CSV http://www.mkyong.com/java/how-to-read-and-parse-csv-file-in-java/ package com.mkyong.util; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class ReadCVS { public static void main(String[] args) { ReadCVS obj = new ReadCVS(); obj.run(); } public void run() { String csvFile = "/Users/mkyong/Downloads/GeoIPCountryWhois.csv"; BufferedReader br = null; String line = ""; String cvsSplitBy = ","; try { br = new BufferedReader(new FileReader(csvFile)); while ((line = br.readLine()) != null) { // use comma as separator String[] country = line.split(cvsSplitBy); System.out.println("Country [code= " + country[4] + " , name=" + country[5] + "]"); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("Done"); } } 14.6 Un esempio di scrittura su file CSV http://stackoverflow.com/questions/17010222/how-do-i-append-text-to-a-csv-txt-file-inprocessing import java.io.File; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.PrintWriter; import java.io.IOException; /** * * @author Prof.G */ public class WriteToCSV { String outFilename = "out.txt"; void setup(){ // Write some text to the file for(int i=0; i<10; i++){ appendTextToFile(outFilename, "Text " + i); } } /** * Appends text to the end of a text file located in the data directory, * creates the file if it does not exist. * Can be used for big files with lots of rows, * existing lines will not be rewritten */ void appendTextToFile(String filename, String text){ File f = new File(filename); if(!f.exists()){ createFile(f); } try { PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(f, true))); out.println(text); out.close(); }catch (IOException e){ e.printStackTrace(); } } /** * Creates a new file including all subfolders */ void createFile(File f){ File parentDir = f.getParentFile(); try{ parentDir.mkdirs(); f.createNewFile(); }catch(Exception e){ e.printStackTrace(); } } 14.7 Un esempio di uso di PrintWriter e BufferedReader http://docs.oracle.com/javase/tutorial/essential/io/charstreams.html import java.io.FileReader; import java.io.FileWriter; import java.io.BufferedReader; import java.io.PrintWriter; import java.io.IOException; public class CopyLines { public static void main(String[] args) throws IOException { BufferedReader inputStream = null; PrintWriter outputStream = null; try { inputStream = new BufferedReader(new FileReader("xanadu.txt")); outputStream = new PrintWriter(new FileWriter("characteroutput.txt")); String l; while ((l = inputStream.readLine()) != null) { outputStream.println(l); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } } 14.8 Serializzazione di oggetti Seguire il cap. 17 del libro “Java How to Program” e integrare con i link: http://www.tutorialspoint.com/java/java_serialization.htm http://www.vogella.com/tutorials/JavaSerialization/article.html http://www.javapractices.com/topic/TopicAction.do?Id=45 (implementing serializable) http://www.javapractices.com/topic/TopicAction.do?Id=57 (reading and writing serialized objects) http://www.tutorialspoint.com/listtutorial/How-to-use-transient-variable-in-Java-withExample/3735 (using transient variables) http://www.coderanch.com/t/277986/java-io/java/detect-file (end of file in serilized object files) http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serialTOC.html 14.9 Progetto di una rubrica di contatti Per creare un'applicazione multi-tab seguire i tutorial seguenti: http://docs.oracle.com/javase/tutorial/uiswing/components/tabbedpane.html https://blogs.oracle.com/geertjan/entry/tabs_in_netbeans_ide Per realizzare liste cliccabili seguire i seguenti tutorial: http://stackoverflow.com/questions/14625091/create-a-list-of-entries-and-make-each-entryclickable http://docs.oracle.com/javase/tutorial/uiswing/components/list.html http://docs.oracle.com/javase/tutorial/uiswing/components/tree.html#create 15 Riferimenti per Java Documentazione ufficiale Oracle - Learning the Java Language: Table of Contents: http://docs.oracle.com/javase/tutorial/java/TOC.html Guida rapida con esempi: Tutorialspoint.com http://www.tutorialspoint.com/java/ La documentazione ufficiale delle API e dei tools Java: http://docs.oracle.com/javase/7/docs/