You are on page 1of 31

M.

RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
Unit V

ADVANCED TECHNIQUESJAR file format creation –Internationalization –Swing Programming


–Advanced java techniques.

You can create a JAR file using the following command.

jar cf jar-file input-file(s)

You can also create JAR files using IDE’s. To create a JAR file using eclipse follow the procedure
given below.

Open the Jar File wizard

The Jar File wizard can be used to export the content of a project into a jar file. To bring up the Jar
File wizard −

In the Package Explorer select the items that you want to export. If you want to export all the
classes and resources in the project just select the project.

Click on the File menu and select Export.

In the filter text box of the first page of the export wizard type in JAR.

Under the Java category select JAR file.

Click on Next.

Using the Jar File wizard

In the JAR File Specification page −

Enter the JAR file name and folder.

The default is to export only the classes. To export the source code also, click on the Export Java
source files and resources checkbox.

Internationalization and Localization in Java

Internationalization and Localization

Understanding the culturally dependent data

Locale class

Example of Local class that prints the informations of the default locale
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
Example of Local class that prints english in different languages

Example of Local class that print display language of many locales

Internationalization is also abbreviated as I18N because there are total 18 characters between the
first letter 'I' and the last letter 'N'.

Internationalization is a mechanism to create such an application that can be adapted to different


languages and regions.

Internationalization is one of the powerful concept of java if you are developing an application and
want to display messages, currencies, date, time etc. according to the specific region or language.

Localization is also abbreviated as I10N because there are total 10 characters between the first
letter 'L' and last letter 'N'. Localization is the mechanism to create such an application that can be
adapted to a specific language and region by adding locale-specific text and component.

Understanding the culturally dependent data before starting internationalization

Before starting the internationalization, Let's first understand what are the informations that differ
from one region to another. There is the list of culturally dependent data:

Messages

Dates

Times

Numbers

Currencies

Measurements

Phone Numbers

Postal Addresses

Labels on GUI components etc.

Importance of Locale class in Internationalization


M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
An object of Locale class represents a geographical or cultural region. This object can be used to get
the locale specific information such as country name, language, variant etc.

Example of Local class that prints the informations of the default locale

In this example, we are displaying the informations of the default locale. If you want to get the
informations about any specific locale, comment the first line statement and uncomment the second
line statement in the main method.

import java.util.*;

public class LocaleExample {

public static void main(String[] args) {

Locale locale=Locale.getDefault();

//Locale locale=new Locale("fr","fr");//for the specific locale

System.out.println(locale.getDisplayCountry());

System.out.println(locale.getDisplayLanguage());

System.out.println(locale.getDisplayName());

System.out.println(locale.getISO3Country());

System.out.println(locale.getISO3Language());

System.out.println(locale.getLanguage());

System.out.println(locale.getCountry());

Output:United States

English

English (United States)

USA
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
eng

en

US

Example of Local class that prints english in different languages

In this example, we are displaying english language in different language. Let's see how english is
written in french and spanish languages.

import java.util.*;

public class LocaleExample2 {

public static void main(String[] args) {

Locale enLocale = new Locale("en", "US");

Locale frLocale = new Locale("fr", "FR");

Locale esLocale = new Locale("es", "ES");

System.out.println("English language name (default): " +

enLocale.getDisplayLanguage());

System.out.println("English language name in French: " +

enLocale.getDisplayLanguage(frLocale));

System.out.println("English language name in spanish: " +

enLocale.getDisplayLanguage(esLocale));

JAVA TECHNIQUES

1. Covariant Return Types

Even the most introductory Java how-to book will include pages of material on inheritance,
interfaces, abstract classes, and method overriding, but rarely do even advanced texts explore the
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
more intricate possibilities when overriding a method. For example, the following snippet will not
come as a surprise to even the most novice Java developer:

public interface Animal {

public String makeNoise();

public class Dog implements Animal {

@Override

public String makeNoise() {

return "Woof";

10

11

12
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE

13

public class Cat implements Animal {

14

15

@Override

16

public String makeNoise() {

17

return "Meow";

18

19

This is the fundamental concept of polymorphism: A method on an object can be called according to
its interface (Animal::makeNoise), but the actual behavior of the method call depends on the
implementation type (Dog::makeNoise). For example, the output of the following method will
change depending on if a Dog object or a Cat object is passed to the method:

public class Talker {

public static void talk(Animal animal) {


M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
4

System.out.println(animal.makeNoise());

Talker.talk(new Dog()); // Output: Woof

Talker.talk(new Cat()); // Output: Meow

10

While this is a technique commonly used in many Java applications, there is a less well-known
action that can be taken when overriding a method: Altering the return type. Although this may
appear to be an open-ended way to override a method, there are some serious constraints on the
return type of an overridden method. According to the Java 8 SE Language Specification (pg. 248):

If a method declaration d 1 with return type R 1 overrides or hides the declaration of another
method d 2 with return type R 2, then d 1 must be return-type-substitutable for d 2, or a compile-
time error occurs.

where a return-type-substitutable (Ibid., pg. 240) is defined as

If R1 is void then R2 is void

If R1 is a primitive type then R2 is identical to R1

If R1is a reference type then one of the following is true:

R1 adapted to the type parameters of d2 is a subtype of R2.


M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
R1 can be converted to a subtype of R2 by unchecked conversion

d1 does not have the same signature as d2 and R1 = |R2|

Arguably the most interesting case is that of Rules 3.a. and 3.b.: When overriding a method, a
subtype of the return type can be declared as the overridden return type. For example:

public interface CustomCloneable {

public Object customClone();

public class Vehicle implements CustomCloneable {

private final String model;

public Vehicle(String model) {

10

this.model = model;

11
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
}

12

13

@Override

14

public Vehicle customClone() {

15

return new Vehicle(this.model);

16

17

18

public String getModel() {

19

return this.model;

20

21

22

23

Vehicle originalVehicle = new Vehicle("Corvette");


M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
24

Vehicle clonedVehicle = originalVehicle.customClone();

25

System.out.println(clonedVehicle.getModel());

Although the original return type of clone() is Object, we are able to call getModel() on our
cloned Vehicle (without an explicit cast) because we have overridden the return type
of Vehicle::clone to be Vehicle. This removes the need for messy casts, where we know that the
return type we are looking for is a Vehicle, even though it is declared to be an Object (which
amounts to a safe cast based on a priori information but is strictly speaking unsafe):

Vehicle clonedVehicle = (Vehicle) originalVehicle.customClone();

Note that we can still declare the type of the vehicle to be a Object and the return type would revert
to its original type of Object:

Object clonedVehicle = originalVehicle.customClone();

System.out.println(clonedVehicle.getModel()); // ERROR: getModel not a method of Object

Note that the return type cannot be overloaded with respect to a generic parameter, but it can be
with respect to a generic class. For example, if the base class or interface method returns
a List<Animal>, the return type of a subclass may be overridden to ArrayList<Animal>, but it may
not be overridden to List<Dog>.

2. Intersectional Generic Types

Creating a generic class is an excellent way of creating a set of classes that interact with composed
objects in a similar manner. For example, a List<T> simply stores and retrieves objects of
type T without an understanding of the nature of the elements it contains. In some cases, we want
to constrain our generic type parameter (T) to have specific characteristics. For example, given the
following interface
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
1

public interface Writer {

public void write();

We may want to create a specific collection of Writers in following with the Composite Pattern:

public class WriterComposite<T extends Writer> implements Writer {

private final List<T> writers;

public WriterComposite(List<T> writers) {

this.writers = writers;

9
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
@Override

10

public void write() {

11

for (Writer writer: this.writers) {

12

writer.write();

13

14

15

We can now traverse a tree of Writers, not knowing whether the specific Writer we encounter is a
standalone Writer (a leaf) or a collection of Writers (a composite). What if we also wanted our
composite to act as a composite for readers as well as writers? For example, if we had the following
interface

public interface Reader {

public void read();

How could we modify our WriterComposite to be a ReaderWriterComposite? One technique would


be to create a new interface, ReaderWriter, that fuses the Reader and Writer interface together:
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
1

public interface ReaderWriter extends Reader, Writer {}

Then we can modify our existing WriterComposite to be the following:

public class ReaderWriterComposite<T extends ReaderWriter> implements ReaderWriter {

private final List<T> readerWriters;

public ReaderWriterComposite(List<T> readerWriters) {

this.readerWriters = readerWriters;

@Override

10

public void write() {

11
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
for (Writer writer: this.readerWriters) {

12

writer.write();

13

14

15

16

@Override

17

public void read() {

18

for (Reader reader: this.readerWriters) {

19

reader.read();

20

21

22

}
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
Although this does accomplish our goal, we have created bloat in our code: We created an interface
with the sole purpose of merging two existing interfaces together. With more and more interfaces,
we can start to see a combinatoric explosion of bloat. For example, if we create a
new Modifier interface, we would now need to create ReaderModifier, WriterModifier,
and ReaderWriter interfaces. Notice that these interfaces do not add any functionality: They simply
merge existing interfaces.

To remove this bloat, we would need to be able to specify that our ReaderWriterComposite accepts
generic type parameters if and only if they are both Reader and Writer. Intersectional generic types
allow us to do just that. In order to specify that the generic type parameter must implement both
the Reader and Writer interfaces, we use the & operator between the generic type constraints:

public class ReaderWriterComposite<T extends Reader & Writer> implements Reader, Writer {

private final List<T> readerWriters;

public WriterComposite(List<T> readerWriters) {

this.readerWriters = readerWriters;

@Override
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
10

public void write() {

11

for (Writer writer: this.readerWriters) {

12

writer.write();

13

14

15

16

@Override

17

public void read() {

18

for (Reader reader: this.readerWriters) {

19

reader.read();

20

21

22
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
}

Without bloating our inheritance tree, we are now able to constrain our generic type parameter to
implement multiple interfaces. Note that the same constraint can be specified if one of the
interfaces is an abstract class or concrete class. For example, if we changed our Writer interface into
an abstract class resembling the following

public abstract class Writer {

public abstract void write();

We can still constrain our generic type parameter to be both a Reader and a Writer, but
the Writer (since it is an abstract class and not an interface) must be specified first (also note that
our ReaderWriterComposite now extends the Writer abstract class
and implements the Reader interface, rather than implementing both):

public class ReaderWriterComposite<T extends Writer & Reader> extends Writer implements
Reader {

// Same class body as before

}
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
It is also important to note that this intersectional generic type can be used for more than two
interfaces (or one abstract class and more than one interface). For example, if we wanted our
composite to also include the Modifier interface, we could write our class definition as follows:

public class ReaderWriterComposite<T extends Reader & Writer & Modifier> implements Reader,
Writer, Modifier {

private final List<T> things;

public ReaderWriterComposite(List<T> things) {

this.things = things;

@Override

10

public void write() {

11

for (Writer writer: this.things) {


M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
12

writer.write();

13

14

15

16

@Override

17

public void read() {

18

for (Reader reader: this.things) {

19

reader.read();

20

21

22

23

@Override

24
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
public void modify() {

25

for (Modifier modifier: this.things) {

26

modifier.modify();

27

28

29

Although it is legal to perform the above, this may be a sign of a code smell (an object that is
a Reader, a Writer, and a Modifier is likely to be something much more specific, such as a File).

For more information on intersectional generic types, see the Java 8 language specification.

3. Auto-Closeable Classes

Creating a resource class is a common practice, but maintaining the integrity of that resource can be
a challenging prospect, especially when exception handling is involved. For example, suppose we
create a resource class, Resource, and want to perform an action on that resource that may throw
an exception (the instantiation process may also throw an exception):

public class Resource {

public Resource() throws Exception {

4
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
System.out.println("Created resource");

public void someAction() throws Exception {

System.out.println("Performed some action");

10

11

public void close() {

12

System.out.println("Closed resource");

13

14

In either case (if the exception is thrown or not thrown), we want to close our resource to ensure
there are no resource leaks. The normal process is to enclose our close() method in a finally block,
ensuring that no matter what happens, our resource is closed before the enclosed scope of
execution is completed:
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
1

Resource resource = null;

try {

resource = new Resource();

resource.someAction();

catch (Exception e) {

System.out.println("Exception caught");

10

finally {

11

resource.close();

12

}
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
By simple inspection, there is a lot of boilerplate code that detracts from the readability of the
execution of someAction() on our Resource object. To remedy this situation, Java 7 introduced
the try-with-resources statement, whereby a resource can be created in the try statement and is
automatically closed before the try execution scope is left. For a class to be able to use the try-with-
resources, it must implement the AutoCloseable interface:

public class Resource implements AutoCloseable {

public Resource() throws Exception {

System.out.println("Created resource");

public void someAction() throws Exception {

System.out.println("Performed some action");

10

11

@Override
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
12

public void close() {

13

System.out.println("Closed resource");

14

15

With our Resource class now implementing the AutoCloseable interface, we can clean up our code
to ensure our resource is closed prior to leaving the try execution scope:

try (Resource resource = new Resource()) {

resource.someAction();

catch (Exception e) {

System.out.println("Exception caught");

}
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
Compared to the non-try-with-resources technique, this process is much less cluttered and
maintains the same safety (the resource is always closed upon completion of the try execution
scope). If the above try-with-resources statement is executed, we obtain the following output:

Created resource

Performed some action

Closed resource

In order to demonstrate the safety of this try-with-resources technique, we can change


our someAction() method to throw an Exception:

public class Resource implements AutoCloseable {

public Resource() throws Exception {

System.out.println("Created resource");

public void someAction() throws Exception {


M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
8

System.out.println("Performed some action");

throw new Exception();

10

11

12

@Override

13

public void close() {

14

System.out.println("Closed resource");

15

16

If we rerun the try-with-resources statement again, we obtain the following output:

Created resource

Performed some action

3
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
Closed resource

Exception caught

Notice that even though an Exception was thrown while executing the someAction() method, our
resource was closed and then the Exception was caught. This ensures that prior to leaving
the try execution scope, our resource is guaranteed to be closed. It is also important to note that a
resource can implement the Closeable interface and still use a try-with-resources statement. The
difference between implementing the AutoCloseable interface and the Closeable interface is a
matter of the type of the exception thrown from the close() method
signature: Exception and IOException, respectively. In our case, we have simply changed the
signature of the close() method to not throw an exception.

4. Final Classes and Methods

In nearly all cases, the classes we create can be extended by another developer and customized to
fit the needs of that developer (we can extend our own classes), even it was not our intent for our
classes to be extended. While this suffices for most cases, there may be times when we do not want
a method to be overridden, or more generally, have one of our classes extended. For example, if we
create a File class that encapsulates the reading and writing of a file on the file system, we may not
want any subclasses to override our read(int bytes) and write(String data) methods (if the logic in
these methods is changed, it may cause the file system to become corrupted). In this case, we mark
our non-extendable methods as final:

public class File {

public final String read(int bytes) {

// Execute the read on the file system

return "Some read data";


M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
6

public final void write(String data) {

// Execute the write to the file system

10

11

Now, if another class wishes to override either the read or the write methods, a compilation error is
thrown: Cannot override the final method from File. Not only have we documented that our
methods should not be overridden, but the compiler has also ensured that this intention is enforced
at compile time.

Expanding this idea to an entire class, there may be times when we do not want a class we create to
be extended. Not only does this make every method of our class non-extendable, but it also ensures
that no subtype of our class can ever be created. For example, if we are creating a security
framework that consumes a key generator, we may not want any outside developer to extend our
key generator and override the generation algorithm (the custom functionality may be
cryptographically inferior and compromise the system):

public final class KeyGenerator {

3
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
private final String seed;

public KeyGenerator(String seed) {

this.seed = seed;

public CryptographicKey generate() {

10

// ...Do some cryptographic work to generate the key...

11

12

By making our KeyGenerator class final, the compiler will ensure that no class can extend our class
and pass itself to our framework as a valid cryptographic key generator. While it may appear to be
sufficient to simply mark the generate() method as final, this does not stop a developer from
creating a custom key generator and passing it off as a valid generator. Being that our system is
security-oriented, it is a good idea to be as distrustful of the outside world as possible (a clever
developer might be able to change the generation algorithm by changing the functionality of other
methods in the KeyGenerator class if those methods we present).
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
Although this appears to be a blatant disregard for the Open/Closed Principle (and it is), there is a
good reason for doing so. As can be seen in our security example above, there are many times
where we do not have the luxury of allowing the outside world to do what it wants with our
application and we must be very deliberate in our decision making about inheritance. Writers such
as Josh Bolch even go so far as to say that a class should either be deliberately designed to be
extended or else it should be explicitly closed for extension (Effective Java). Although he purposely
overstated this idea (see Documenting for Inheritance or Disallowing It), he makes a great point:
We should be very deliberate about which of our classes should be extended, and which of our
methods are open for overriding.

Conclusion

While most of the code we write utilizes only a fraction of the capabilities of Java, it suffices to solve
most of the problems that we encounter. There are times though that we need to dig a little deeper
into the language and dust off those forgotten or unknown parts of the language to solve a specific
problem. Some of these techniques, such as covariant return types and intersectional generic types
may be used in one-off situations, while others, such as auto-closeable resources and final methods
and classes can and should be used to more often to produce more readable and more precise code.
Combining these techniques with daily programming practices aids in not only a better
M.RAJALAKSHMI
ASSISTANT PROFESSOR
DEPARTMENT OF COMPUTER SCIENCE (PG)
SANKARA COLLEGE OF SCIENCE AND
COMMERCE
understanding of our intentions but also better, more well-written Java.

You might also like