[Prévia] [Próxima] [Prévia por assunto] [Próxima por assunto]
[Índice cronológico] [Índice de assunto]

repassando: Core Java Technologies Tech Tips, January 4, 2005 (Varargs, Covariant Parameter Types)



>
> Vixe! Se algum dia Java foi uma linguagem simples, essa simplicidade  
> foi definitivamente por água abaixo com a nova versão 1.5. E tudo isso  
> por pressão da bela C# :-)
>
> Vejam duas novas belezinhas do 1.5:
>
> Começar a mensagem reencaminhada:
>> De: "SDN - Core Java Technologies Tech Tips" <sunmail@hermes.sun.com>
>> Data: 4 de janeiro de 2005 19h16min37s GMT-02:00
>> Para: f-kon@spurius.cs.uiuc.edu
>> Assunto: Core Java Technologies Tech Tips, January 4, 2005 (Varargs,  
>> Covariant Parameter Types)
>>
>> Core Java Technologies Tech Tips
>>
>>
>>                       Tips, Techniques, and Sample Code
>>
>>
>> Welcome to the Core Java Technologies Tech Tips for January 4,
>> 2005. Here you'll get tips on using core Java technologies and
>> APIs, such as those in Java 2 Platform, Standard Edition (J2SE).
>>
>> This issue covers:
>>
>>           * Using the Varargs Language Feature
>>           * Covariant Parameter Types
>>
>> These tips were developed using the Java 2 Platform Standard
>> Edition Development Kit 5.0 (JDK 5.0). You can download JDK 5.0
>> at http://enews.sun.com/CTServlet?id=767044851351329170:1104872870546.
>>
>> This issue of the Core Java Technologies Tech Tips is written by
>> Daniel H. Steinberg, Director of Java Offerings for Dim Sum
>> Thinking, Inc, and editor-in-chief for java.net (http://java.net).
>>
>> You can view this issue of the Tech Tips on the Web at
>> http://java.sun.com/developer/JDCTechTips/2005/tt0104.html.
>>
>> See the Subscribe/Unsubscribe note at the end of this newsletter
>> to subscribe to Tech Tips that focus on technologies and products
>> in other Java platforms.
>>
>> For more Java technology content, visit these sites:
>>
>> java.sun.com - The Java technology source for developers. Get the
>> latest Java platform releases, tutorials, newsletters and more.
>>
>> java.net - A web forum where enthusiasts of Java technology can
>> collaborate and build solutions together.
>>
>> java.com - The ultimate marketplace promoting Java technology,
>> applications and services.
>>
>> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
>> USING THE VARARGS LANGUAGE FEATURE
>>
>> Have you ever found yourself in the following situation? You
>> need to pass in many instances of the same object type to
>> a method, but you don't know at compile time how many instances
>> there will be. In the past, the only way to handle this
>> situation was to bundle these objects in an array or other
>> collection. However with J2SE 5.0, you now have the added
>> convenience of using variable arity parameters, known less
>> formally as varargs. In this tip, you will learn why it's a good
>> idea to take advantage of varargs as a client of an API. The tip
>> also cautions against introducing varargs to an API that you
>> create, unless it is warranted.
>>
>> Let's start with an example that illustrates the use of varargs.
>> In the following program, VarGreeter, the method printGreeting()
>> takes an unspecified number of String objects as input. This is
>> indicated by the use of ellipses in the parameter declaration
>> String ... names. In general, a method can have at most one
>> parameter that is a vararg, it must be the last parameter taken
>> by the method, and it is denoted by the object type, a set of
>> ellipses ( ... ), and the name of the variable.
>>
>>   class VarGreeter {
>>
>>        public static void printGreeting(String... names) {
>>           for (String n : names) {
>>               System.out.println("Hello " + n + ". ");
>>           }
>>        }
>>
>>        public static void main(String[] args) {
>>           printGreeting("Paul", "Sue");
>>        }
>>    }
>>
>> In VarGreeter, the method printGreeting() is invoked with the
>> call printGreeting("Paul","Sue"). If you run VarGreeter, you get
>> the message:
>>
>>    Hello Paul.
>>    Hello Sue.
>>
>> Change the line in VarGreeter that calls printGreeting() to have
>> as many String object arguments as you like. Each name that you
>> pass in will be greeted. The printGreeting() method works
>> its way through whatever collection of strings it is passed, and
>> greets each in turn. What is perhaps most striking is that this
>> works if no string is passed. You can see this by changing
>> main() to the following and rerunning VarGreeter.
>>
>>    public static void main(String[] args) {
>>     printGreeting();
>>    }
>>
>> This time you will have no output because there is no one to
>> greet. This example illustrates an appropriate use of varargs.
>> You have an unspecified number of String objects to process in
>> the same way.
>>
>> At compile time a vararg is converted to an array. You can see
>> that the printGreeting() method uses an enhanced for loop to
>> iterate through the array. Using a vararg is different than
>> using an array. The next few examples highlight the similarities
>> and differences. First consider VarGreeter2:
>>
>>    public class VarGreeter2 {
>>       public static void printGreeting(String... names) {
>>           for (String n : names) {
>>               System.out.println("Hello " + n + ". ");
>>           }
>>       }
>>
>>       public static void main(String[] args) {
>>           printGreeting(args);
>>       }
>>    }
>>
>> The key is that you can pass in an array as the parameter. The
>> args variable is an array of type String. Run this version with
>> the command:
>>
>>    java VarGreeter2 Paul Sue
>>
>> and you will get the same output as before:
>>
>>    Hello Paul.
>>    Hello Sue
>>
>> You can experiment with different numbers of command-line
>> arguments. You can even pass in no arguments. Ordinarily, you
>> need a guard clause to make sure that args does not have length
>> zero. When required, you should check for proper input. In this
>> case, there are no such requirements. The printGreeting() method
>> can handle zero or more strings passed as a list of strings or
>> passed as a single array of strings.
>>
>> Now consider VarGreeter3. Here you will see that you can even
>> change the signature of main(). Although there is no reason for
>> doing this, the canonical public static void main(String[] args)
>> can be changed to public static void main(String ... args):
>>
>>    public class VarGreeter3 {
>>
>>        public static void printGreeting(String... names) {
>>           for (String n : names) {
>>                System.out.println("Hello " + n + ". ");
>>           }
>>        }
>>
>>        public static void main(String... args) {
>>           printGreeting(args);
>>        }
>>    }
>>
>> Run VarGreeter3 with the command:
>>
>>    java VarGreeter3 Paul Sue
>>
>> and you will get the same output as before:
>>
>>    Hello Paul.
>>    Hello Sue.
>>
>> Using "main(String... args)" makes it easier to invoke main
>> directly from Java. This can be useful for writing unit tests of
>> your main() method.
>>
>> So far, you have not seen any difference between explicitly
>> declaring printGreeting() using varargs and using an array. Now
>> it's time to look at a difference. Change the signature from
>> printGreeting(String ... names) to printGreeting(String[] names).
>> Notice that in VarGreeter4, you can still pass in an array of
>> strings with the call printGreeting(args):
>>
>>    public class VarGreeter4 {
>>
>>        public static void printGreeting(String[] names) {
>>           for (String n : names) {
>>               System.out.println("Hello " + n + ". ");
>>           }
>>        }
>>
>>        public static void main(String... args) {
>>           printGreeting(args);
>>        }
>>    }
>>
>> You'll still get the same results as before if you run the
>> command:
>>
>>    java VarGreeter4 Paul Sue
>>
>> The difference is if you try to call printGreeting using
>> something like printGreeting("Paul", "Sue") as shown in
>> VarGreeter5:
>>
>>    public class VarGreeter5 {
>>
>>        public static void printGreeting(String[] names) {
>>           for (String n : names) {
>>               System.out.println("Hello " + n + ". ");
>>           }
>>        }
>>
>>        public static void main(String... args) {
>>           printGreeting("Paul", "Sue");
>>        }
>>    }
>>
>> Try compiling VarGreeter5. It won't compile:
>>
>>
>>    javac VarGreeter5.java
>>       VarGreeter5.java:10: printGreeting(java.lang.String[]) in
>>       VarGreeter5 cannot be applied to
>>       (java.lang.String,java.lang.String)
>>           printGreeting("Paul", "Sue");
>>           ^
>>    1 error
>>
>> The lesson is that varargs give you a great deal of flexibility
>> as the client of an API.
>>
>> Warning: The following examples illustrate some of the dangers
>> in introducing varargs to an API. It is at least as important to
>> know when it is inappropriate to use a technique as it is to
>> know when to use it.
>>
>> The GridLayout class has three constructors, one takes four
>> arguments, one takes two arguments, and one takes no arguments.
>> The constructor with two arguments allows a user to specify the
>> number of rows and columns in the resulting grid. The
>> constructor with four arguments also allows a user to specify
>> the size of the horizontal gap between rows and the size of the
>> vertical gap between columns. The two-argument constructor
>> assumes those values to be zero. The no-argument constructor
>> provides defaults for the number of rows and columns. The
>> following program, PreVarGridLayout, is a simplified class with
>> three constructors that are defined in much the same way as for
>> the GridLayout class:
>>
>>    class PreVarGridLayout {
>>
>>       PreVarGridLayout() {
>>          this(1, 1);
>>       }
>>
>>       PreVarGridLayout(int rows, int cols) {
>>          this(rows, cols, 0, 0);
>>       }
>>
>>       PreVarGridLayout(int rows, int cols, int hgap, int vgap) {
>>          System.out.println("Create a grid with rows = "
>>          + rows + " and cols = " + cols + ", the hgap = "
>>          + hgap + " and the vgap = " + vgap + ".");
>>       }
>>
>>       public static void main(String[] args) {
>>          System.out.println("Call no arg constructor:");
>>          new PreVarGridLayout();
>>          System.out.println("Call two arg constructor: 2,3");
>>          new PreVarGridLayout(2, 3);
>>          System.out.println
>>             ("Call four arg constructor: 4,5,6,7");
>>          new PreVarGridLayout(4, 5, 6, 7);
>>       }
>>    }
>>
>> In main(), you can see that each constructor is called. If you
>> run PreVarGridLayout, you will get the following output:
>>
>>    Call no arg constructor:
>>    Create a grid with rows = 1 and cols = 1, the hgap = 0 and
>>    the vgap = 0.
>>    Call two arg constructor: 2,3
>>    Create a grid with rows = 2 and cols = 3, the hgap = 0 and
>>    the vgap = 0.
>>    Call four arg constructor: 4,5,6,7
>>    Create a grid with rows = 4 and cols = 5, the hgap = 6 and
>>    the vgap = 7.
>>
>> You can transform the program to take advantage of varargs, as
>> shown in VarGridLayout:
>>
>>    public class VarGridLayout {
>>
>>       VarGridLayout(Integer... size) {
>>          Object temp[] = {1,1,0,0};
>>          System.arraycopy(size, 0, temp, 0, size.length);
>>          System.out.printf("Create a grid with rows = %d " +
>>          "and cols = %d, the hgap = %d and the vgap = " +
>>          "%d. \n", temp);
>>       }
>>
>>       public static void main(String[] args) {
>>          System.out.println("Call no arg constructor:");
>>          new VarGridLayout();
>>          System.out.println("Call two arg constructor: 2,3");
>>          new VarGridLayout(2, 3);
>>          System.out.println(
>>             "Call four arg constructor: 4,5,6,7");
>>          new VarGridLayout(4, 5, 6, 7);
>>       }
>>     }
>>
>> There are no changes to main(), so the constructors are called
>> in the same way in VarGridLayout as they were in
>> PreVarGridLayout, that is, before varargs were introduced. The
>> difference is that now there is a single constructor. The
>> constructor takes zero, two, or four arguments as before and
>> prints the same output. Notice that this time printf() is used
>> to write to the output stream. The printf() facility uses
>> varargs. This allows you to pass in temp as the last parameter
>> to printf(). The method treats temp as an array of type Integer.
>>
>> But there's a serious problem in using varargs this way. You can
>> see the problem by changing the last line of the main() method
>> in PreVarGridLayout to:
>>
>>    new PreVarGridLayout(4,5,6,7,8);
>>
>> and in VarGridLayout to:
>>
>>    new VarGridLayout(4,5,6,7,8);
>>
>> Recompile PreVarGridLayout:
>>
>>    javac PreVarGridLayout.java
>>    PreVarGridLayout.java:23: cannot find symbol
>>    symbol  : constructor PreVarGridLayout(int,int,int,int,int)
>>    location: class PreVarGridLayout
>>        new PreVarGridLayout(4,5,6,7,8);
>>        ^
>>    1 error
>>
>> You get a compiler error because that signature does not match
>> any of the available constructors for PreVarGridLayout. In fact,
>> if you use an IDE with code sense, it should be able to signal
>> before compile time that this will not compile.
>>
>> Now compile VarGridLayout. You will get no warnings at compile
>> time because your constructor can take zero or more Integer
>> objects as parameters. If you run VarGridLayout, you will get an
>> ArrayIndexOutOfBoundsException.
>>
>> You could provide a check that size.length is either zero, two,
>> or four, and throw your own exception otherwise. The point is
>> that this check is left to you, it is not enforced by the
>> compiler. This means that developers using your code will not
>> have support at compile or writing time for making the correct
>> calls. So the lesson is do not use varargs for this purpose.
>>
>> Another issue in using varargs is knowing which method will be
>> called if there appears to be competing signatures. In the
>> following class, WhichOne, there are three constructors. The
>> first constructor takes a vararg argument, the second takes two
>> int arguments, and the third takes two Integer arguments:
>>
>>    public class WhichOne {
>>
>>
>>       WhichOne(Integer... size) {
>>          System.out.println("Var Args version.");
>>       }
>>
>>       WhichOne(int i, int j) {
>>          System.out.println("Version with int args.");
>>
>>       }
>>
>>       WhichOne(Integer i, Integer j) {
>>          System.out.println("Version with Integer args.");
>>       }
>>
>>       public static void main(String[] args) {
>>          System.out.println("Call w/ two arg:2,3");
>>          new WhichOne(2, 3);
>>          System.out.println("Call w/ Integer two arg: 2,3");
>>          new WhichOne(new Integer(2), new Integer(3));
>>       }
>>
>>    }
>>
>> Run WhichOne and you will see the following output:
>>
>>    Call w/ two arg:2,3
>>    Version with int args.
>>    Call w/ Integer two arg: 2,3
>>    Version with Integer args.
>>
>> Even with the varargs version of the constructor, the most
>> specifically-matching constructor is called. Eliminate the
>> second and third constructors so that WhichOne looks like the
>> following:
>>
>>    public class WhichOne {
>>
>>
>>       WhichOne(Integer... size) {
>>          System.out.println("Var Args version.");
>>       }
>>
>>       public static void main(String[] args) {
>>          System.out.println("Call w/ two arg:2,3");
>>          new WhichOne(2, 3);
>>          System.out.println("Call w/ Integer two arg: 2,3");
>>          new WhichOne(new Integer(2), new Integer(3));
>>       }
>>
>>    }
>>
>> Rerun WhichOne. You should see the following output:
>>
>>    Call w/ two arg:2,3
>>    Var Args version.
>>    Call w/ Integer two arg: 2,3
>>    Var Args version.
>>
>> In the absence of the more specific constructors, the varargs
>> version is called by both new WhichOne(2,3) and by
>> new WhichOne(new Integer(2), new Integer(3)). It is important in
>> constructing an API to make clear to the client how to call the
>> desired method. So the lesson here is avoid using varargs on
>> overloaded methods.
>>
>> To reiterate, the advantage of using varargs is that as
>> a client of an API, you are free to call a method using
>> a sequence of instances of an object of the specified type or an
>> array. You have the freedom to make the call using any number of
>> instances of that type. There are also dangers in using varargs.
>> You might want to avoid providing methods that use varargs when
>> it is important to specify the number of entries or to prevent
>> autoboxing.
>>
>> For more information about varargs, see the document "Varargs"
>> (http://java.sun.com/j2se/1.5.0/docs/guide/language/varargs.html).
>>
>> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
>> COVARIANT PARAMETER TYPES
>>
>> The December 1, 2004 Tech Tip "Covariant Return Types"
>> (http://java.sun.com/developer/JDCTechTips/2004/tt1201.html#2)
>>
>> presented an example of using this new J2SE 5.0 facility. This
>> facility allows you to create methods in a subclass that return
>> an object whose type is a subtype of that returned by the
>> method it overrides. In the following tip you will see the
>> difficulties involved in extending this capability to method
>> parameters.
>>
>> The example discussed here was provided by Sun Engineer
>> Peter von der Ahe, and is based on a
>> suggestion he received from computational theologist
>> Gilad Bracha. The intent is to demonstrate that although there
>> are good reasons for implementing covariant return types,
>> implementing covariant method parameters is unsound.
>>
>> The basic mechanism outlined in the December tip began with two
>> pairs of classes. In this tip, let's simplify the classes:
>>
>>    class A {}
>>
>>    class B extends A {}
>>
>> Now, define a class, Super, that contains a method that returns
>> an object of type A:
>>
>>    class Super {
>>       public A someMethod(){
>>       return new A();
>>       }
>>    }
>>
>> Next, create a class, Sub, that extends Super. In J2SE 1.4 and
>> previous releases, you could not override someMethod() by
>> changing its  return type. In particular, the following code
>> would not compile:
>>
>>    class Sub extends Super {
>>       public B someMethod() {
>>       return new B();
>>       }
>>    }
>>
>> Previous to J2SE 5.0, the compiler would issue a message like
>> this:
>>
>>    someMethod() in Sub cannot override someMethod() in Super;
>>    attempting to use incompatible return type.
>>
>> In J2SE 5.0, this code compiles and the narrower type B is
>> a legal return type for the someMethod() method in the
>> subclass, Sub.
>>
>> Is it possible to perform a similar narrowing of type for
>> a method parameter? Let's try by creating a "circle of life"
>> example with the Food and Animal interfaces:
>>
>>    interface Food {
>>       int glycemicIndex();
>>    }
>>
>>    interface Animal {
>>       void eat(Food food);
>>    }
>>
>> There are no problems in implementing the Food interface, as
>> the following class, Grass, shows:
>>
>>    class Grass implements Food {
>>       public int glycemicIndex() {
>>          return 20; // this is a guess, don't base your diet
>>                     // on this
>>       }
>>    }
>>
>> An antelope is an animal that eats food and it is itself food
>> for an animal such as a lion. Here are versions of compilable
>> classes that model the antelope and lion. The classes implement
>> the eat() method declared in the Animal interface:
>>
>>    class Antelope implements Food, Animal {
>>       public void eat(Food food) {
>>       }
>>
>>       public int glycemicIndex() {
>>          return 5; // this is a guess, don't base your diet
>>                    // on this
>>       }
>>    }
>>
>>    class Lion implements Animal {
>>     public void eat(Food food) { }
>>    }
>>
>> The problem with this is that the code allows an Antelope and
>> a Lion to eat anything of type Food. You might be tempted to
>> try the following to restrict the Antelope to only eat objects
>> of type Grass, and the Lion to only eat objects of type
>> Antelope:
>>
>>    class Antelope implements Food, Animal {
>>       public void eat(Grass food) {
>>       }
>>
>>       public int glycemicIndex() {
>>          return 5; // this is a guess, don't base your diet
>>                    // on this
>>       }
>>    }
>>
>>     class Lion implements Animal {
>>        public void eat(Antelope food) { }
>>     }
>>
>> Unfortunately, however, this will no longer compile. You are
>> allowed to add the method with the signature eat(Grass food) to
>> the Antelope class, but you cannot declare that Antelope
>> implements Food. That's because you are not implementing the
>> signature declared in the interface. Your compiler issues
>> a warning that includes something like this.
>>
>>    Antelope is not abstract and does not override abstract
>>    method eat(Food) in Animal
>>
>>    Lion is not abstract and does not override abstract method
>>    eat(Food) in Animal
>>
>> A solution involves the use of Generics. Start with the
>> declaration of Generics, and declare eat() so that its argument
>> is at least of type Food:
>>
>>    public interface Food {
>>       int glycemicIndex();
>>    }
>>
>>    public interface Animal <F extends Food> {
>>       public void eat(F food);
>>    }
>>
>> This now more accurately represents the idea that an object of
>> type Animal eats a particular type of Food. The implementation of
>> Grass does not need to change:
>>
>>    public class Grass implements Food {
>>       public int glycemicIndex() {
>>          return 20; //this is a guess, don't base your diet
>>                     // on this
>>       }
>>    }
>>
>> The implementation of Antelope and of Lion can now specify the
>> type of Food that each Animal can eat:
>>
>>    public class Antelope implements Food, Animal<Grass> {
>>       public int glycemicIndex() {
>>          return 5; //this is a made up number
>>       }
>>
>>       public void eat(Grass food) {
>>       }
>>    }
>>
>>    public class Lion implements Animal<Antelope> {
>>
>>       public void eat(Antelope food) {
>>       }
>>    }
>>
>> You can now exercise these classes, again by employing generics:
>>
>>    class Example {
>>       static <F extends Food> void feed
>>         (Animal<F> animal, F food) {
>>             animal.eat(food);
>>         }
>>
>>       public static void main(String... args) {
>>          feed(new Lion(), new Antelope());
>>       }
>>    }
>>
>> There is nothing built into J2SE 5.0 to automatically handle
>> covariant parameter types in the same way that covariant return
>> types are now accommodated. You can, however, follow the pattern
>> in this tip to employ generics to allow for extending the type
>> being passed as a parameter to a method.
>>
>> .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
>>
>> PRIVACY STATEMENT:
>> Sun respects your online time and privacy (http://sun.com/privacy).
>> You have received this based on your e-mail preferences. If you
>> would prefer not to receive this information, please follow the
>> steps at the bottom of this message to unsubscribe.
>>
>> Please read our Terms of Use and Licensing policies:
>> http://www.sun.com/share/text/termsofuse.html
>> http://developers.sun.com/dispatcher.jsp?uid=6910008
>>
>> * FEEDBACK
>>   Comments? Please enter your feedback on the Tech Tips at:
>>
>>      http://developers.sun.com/contact/feedback.jsp?category=sdn
>>
>>
>> * SUBSCRIBE/UNSUBSCRIBE
>>
>>   Subscribe to other Java developer Tech Tips:
>>
>>   - Enterprise Java Technologies Tech Tips. Get tips on using
>>     enterprise Java technologies and APIs, such as those in the
>>     Java 2 Platform, Enterprise Edition (J2EE).
>>   - Wireless Developer Tech Tips. Get tips on using wireless
>>     Java technologies and APIs, such as those in the Java 2
>>     Platform, Micro Edition (J2ME).
>>
>>   To subscribe to these and other SDN publications:
>>   - Go to the Sun Developer Network - Subscriptions page,
>>      
>> (https://softwarereg.sun.com/registration/developer/en_US/ 
>> subscriptions),
>>     choose the newsletters you want to subscribe to and click
>>     "Submit".
>>   - To unsubscribe, go to the Subscriptions page,
>>      
>> (https://softwarereg.sun.com/registration/developer/en_US/ 
>> subscriptions),
>>     uncheck the appropriate checkbox, and click "Submit".
>>
>>   - To use our one-click unsubscribe facility, see the link at
>>     the end of this email:
>>
>> - ARCHIVES
>> You'll find the Core Java Technologies Tech Tips archives at:
>>
>> http://java.sun.com/developer/TechTips/index.html
>>
>>
>> - COPYRIGHT
>> Copyright 2005 Sun Microsystems, Inc. All rights reserved.
>> 4150 Network Circle, Santa Clara, California 95054 USA.
>>
>> This document is protected by copyright. For more information, see:
>>
>> http://java.sun.com/jdc/copyright.html
>>
>>
>> Core Java Technologies Tech Tips
>> January 4, 2005
>
>
>