views:

3166

answers:

30

Java is nearing version 7. It occurs to me that there there must be plenty of textbooks and training manuals kicking around that teach methods based on older versions of Java; where the methods taught, would have far better solutions now.

What are some boilerplate code situations, especially ones that you see people implement through force of habit, that you find yourself refactoring to utilize the latest versions of Java?

+39  A: 

Generics and no longer needing to create an iterator to go through all elements in a collection. The new version is much better, easier to use, and easier to understand.

EDIT:

Before:

List l = someList;
Iterator i = l.getIterator();
while (i.hasNext()) {
    MyObject o = (MyObject)i.next();
}

After

List<MyObject> l = someList;
for (MyObject o : l) {
    //do something
}
Elie
The underlying mechanism of the new for loop still creates an iterator, you just don't have to write out the boilerplate code for it.
sk
I was just about to say that, sk
i3ensays
yes, but the point is, in your code, you DON'T have to write it, and that is a nice feature of the newer versions of Java. Just because in the background nothing has changed doesn't mean that it isn't a good feature.
Elie
Ah, there's also the fact that generics offer type safety for collections. Refactoring 1.4 code to 1.5 makes a huge difference for readability for this reason, in my mind.
Marc Bollinger
Post a bit of before-and-after example code please.
Kevin Conner
You mentioned Generics but your loop sample still uses Object. Tsk Tsk. :)
Mr. Shiny and New
Good point... changed that.
Elie
+11  A: 

Q1: Well, the most obvious situations are in the generics / type specific collections. The other one that immediately springs to mind is the improved for loop, which I feel is a lot cleaner looking and easier to understand.

Q2: In general, I have been bundling the JVM along side of my application for customer-facing apps. This allows us to use new language features without having to worry about JVM incompatibility.

If I were not bundling the JRE, I would probably stick to 1.4 for compatibility reasons.

James Van Huis
strive to post one answer at a time (so each can be ranked individually)
i3ensays
I'm not sure what you mean...?
James Van Huis
He meant that you should have split this answer into two seperate answers.
Christian Vest Hansen
Ahhh, that makes sense. Thanks.
James Van Huis
+8  A: 

Converting a number to a String:

String s = n + "";

In this case I think there has always been a better way of doing this:

String s = String.valueOf(n);
Kip
String.valueOf(n) takes care of that last problem.
Michael Myers
String s = n + ""; is a bad habit because it will always create a new object. valueOf is smarter about that.
cynicalman
You're right, it's too early in the morning I completely forgot about String.valueOf(n). I've updated it.
Kip
valueOf: since at least version 1.3
matt b
Don't forget the static Integer.toString(int, int) method. This gives you a String representation of the first parameter in the base specified by the second parameter.
Will Wagner
This is one of those things that's so common I wish the compiler would just handle it for you.
Chris Kessel
@chriskes it does, just use the s=n+""! Until someone can actually demonstrate production code where these stupid little tweaks actually speed something up, I'm going to stick with not outsmarting the compiler by trying to understand how it implements these things (which change every release)
Bill K
+10  A: 

A simple change in since 1.5 but makes a small difference - in the Swing API accessing the contentPane of a JFrame:

myframe.getContentPane().add(mycomponent);

becomes

myframe.add(mycomponent);

And of course the introduction of Enums has changed the way many applications that used constants in the past behave.

String.format() has greatly improved String manipulation and the ternary if statement is quite helpful in making code easier to read.

Vincent Ramdhanie
If I recall correctly, myframe.add(...) used to cause a warning message to be printed to stderr.
James Schek
Nope, in pre-1.5, the JRE would throw an error at Runtime:Exception in thread "main" java.lang.Error: Do not use javax.swing.JFrame.add() use javax.swing.JFrame.getContentPane().add() instead
dogbane
+60  A: 

Enums. Replacing

public static final int CLUBS = 0;
public static final int DIAMONDS = 1;
public static final int HEARTS = 2;
public static final int SPADES = 3;

with

public enum Suit { 
  CLUBS, 
  DIAMONDS, 
  HEARTS, 
  SPADES 
}
Eek
Never actually needed one.
alamar
the new stuff is much prettier. ;-)
djangofan
+21  A: 

Here is one that I see:

String.split() versus StringTokenizer.

StringTokenizer is not recommended for new code, but I still see people use it.

As for compatibility, Sun makes a huge effort to have Java be backwards and forwards compatible. That partially accounts for why generics are so complex. Deprecation is also supposed to help ease transitions from old to new code.

Julien Chastang
:( Hadn't heard that StringTokenizer was no longer recommended. I love the StringTokenizer! Great for decoding switched inputs and complex statements. Things where it seems like it would be a lot hard to do with split().
Brian Knoblauch
@Brian Knoblauch I think String.split() is supposed to replace the 80% case of using StringTokenizer with a single token and the default flags. You can still use StringTokenizer on more complex situations as you mentioned.
martinatime
JavaDoc says "StringTokenizer is a legacy class that is retained for compatibility reasons although its use is discouraged in new code. It is recommended that anyone seeking this functionality use the split method of String or the java.util.regex package instead."
Patrick
@Brian Split and a minimal loop should be close to StringTokenizer, but I agree. Also I despise regular expressions, which split seems to require. I also couldn't figure out how to get split to return tokens or not (probably part of the RE syntax I don't want to deal with).
Bill K
@Bill: No, they left that out--in other languages, you can return the delimiters by wrapping the regex in parentheses, but not Java. Can't say I blame 'em though, considering how many people don't even realize split _uses_ regexes.
Alan Moore
+33  A: 

Using local variables of type StringBuffer to perform string concatenation. Unless synchronization is required, it is now recommended to use StringBuilder instead, because this class offers better performance (presumably because it is unsynchronized).

Don
ArrayList is preferred to Vector, and HashMap to Hashtable, for the same reason.
Kip
Some modern JITs can tell when a lock is thread-local and optimize it away, making StringBuffer as fast as StringBuilder... but I don't quite recall which JVM(s), exactly, that did this.
Christian Vest Hansen
it is called escape analysis - and should be part of jre6
Andreas Petersson
+15  A: 

Using local variables of type Vector to hold a list of objects. Unless synchronization is required, it is now recommended to use a List implementation such as ArrayList instead, because this class offers better performance (because it is unsynchronized).

Don
Is this something new? I thought this was always the case.
Liam
Depends on how you define "new". I'm stuck writing Java 1.1 code (so we can support the people who refuse to upgrade from the MS JVM), and the unsynchronized collections didn't exist back then.
Herms
This is not entirely correct. A Vector is a distinctly different data type from an ArrayList--i.e. look at the setSize method. That said, in most cases a vector type isn't necessary.
James Schek
It's a different class (obviously), but they're both implementations of List. My point was that often a Vector is often used where another List implementation could/should be used instead
Don
Sorry--poorly worded on my part. I complete agree with your point. Just trying to point out that a Vector is a resizable array (like STL) where an ArrayList isn't. In those cases, ArrayList won't work. This catches a lot of C++ converts because they are told ArrayList==Vector.
James Schek
java.util.Stack is also the devil.
Sean Reilly
+12  A: 

Explicit conversion between primitive and wrapper types (e.g. Integer to int or vice versa) which is taken care of automatically by autoboxing/unboxing since Java 1.5.

An example is

Integer myInteger = 6;
int myInt = myInteger.intValue();

Can simply be written as

Integer myInteger = 6;
int myInt = myInteger;

But watch out for NullPointerExceptions :)

Don
Surely that should be: Integer myInteger = new Integer(6);:)
Richie_W
No, it's perfectly legal to assign a literal value to an Integer. Your code would work too, but I prefer to use literals for the sake of brevity.
Don
I think he means that the "before" example should say "new Integer(6)", and the "after" example is already fine.
Kevin Conner
Also beware that a boxed int is not equal to a boxed long, even if it has the same value.
starblue
+1  A: 

New version of Java rarely break existing code, so just leave old code alone and focus on how the new feature makes your life easier.

If you just leave old code alone, then writing new code using new features isn't as scary.

Pyrolistical
That's fine if your old code is perfectly bug-free, never needing any maintenance!
Liam
All code everyone ever creates is legacy code. What do good boyscouts do to the campsite? :)
Esko
I kind of agree with this. If I'm in a function fixing bugs or adding functionality, I'll refactor it as well. But I'll never refactor something that still works just to utilize new features.
Thomas Owens
Most of us probably aren't fortunate enough to be able to 'leave old code alone'. And when you do touch it, it'd be silly not to use the new features (unless, of course, that old code needs to work in 1.4 or something).
Jonik
+9  A: 

Generic collections make coding much more bug-resistant. OLD:

Vector stringVector = new Vector();
stringVector.add("hi");
stringVector.add(528); // oops!
stringVector.add(new Whatzit());  // Oh my, could spell trouble later on!

NEW:

ArrayList<String> stringList = new ArrayList<String>();
stringList.add("hello again");
stringList.add(new Whatzit()); // Won't compile!
Ogre Psalm33
+22  A: 

Older code using Thread instead of the many other alternatives to Thread... these days, very little of the code I run across still needs to use a raw thread. They would be better served by a level of abstraction, particular Callable/Futures/Executors.

See:

java.util.Timer

javax.swing.Timer

java.util.concurrent.*

James Schek
+9  A: 

Using Iterator:

List list = getTheList();
Iterator iter = list.iterator()
while (iter.hasNext()) {
  String s = (String) iter.next();
    // .. do something
}

Or an alternate form sometimes seen:

List list = getTheList();
for (Iterator iter = list.iterator(); iter.hasNext();) {
  String s = (String) iter.next();
  // .. do something
}

Is now all replaced with:

List<String> list = getTheList();
for (String s : list) {
  // .. do something
}
+1  A: 

String comparisons, really old school Java programmers I've met would do:

String s1 = "...", s2 = "...";

if (s1.intern() == s2.intern()) {
    ....
}

(Supposedly for performance reasons)

Whereas these days most people just do:

String s1 = "...", s2 = "...";

if (s1.equals(s2)) {
    ....
}
Jack Leow
intern is a memory thing not a speed thing, indeed intern will make your code slower as it is an expensive operation (unless they have fixed that recently... actually I'll know soon I am about to do something that will test it)
TofuBeer
Also, in this case s1 and s2 are literal strings so they are automatically intern'ed according to the spec. So the first example is actually a waste of typing.
Alex Miller
From the beginning, the proper way to compare Strings for equality was almost always equals(). What intern() does is take a String and return a reference to a canonical String in a pool maintained by class String. All literals are added to this pool too. Interned Strings and literals *can* be compared for identity with == at high speed. In a thorough optimization of a large server, I found two spots where intern() led to measurable performance gains. In 99.99% of all cases, though, use equals() to prevent bugs and to keep the literal pool smaller.
Jim Ferrans
+17  A: 

VARARGS can be useful too.

For example, you can use:

public int add(int... numbers){
    int sum = 0 ;
    for (int i : numbers){
        sum+=i;
    }
    return sum ;
}

instead of:

public int add(int n1, int n2, int n3, int n4) ;

or

public int add(List<Integer> numbers) ;
dogbane
I've found varargs to be immensely useful. Hard to imagine what we did before. There's still lots of legacy APIs out there with overloaded methods with 1 arg, 2 args, 3 args, 4 args, and object[] args. sigh.
Mr. Shiny and New
I didn't know that was in java! was it added in 5 or 6? I haven't done much java aside from academic work and j2me which is 1.4 based :(
Kevlar
It was added in 5.
dogbane
I'm now a wiser man! Thanks!
Averroes
+1  A: 

Using Vector instead of the new Collections.

Using classes instead of enums

 public class Enum
  {
      public static final Enum FOO = new Enum();
      public static final Enum BAR = new Enum();
  }

Using Thread instead of the new java.util.concurrency package.

Using marker interfaces instead of annotations

TofuBeer
+3  A: 

Changing JUnit 3-style tests:

class Test extends TestCase {
    public void testYadaYada() { ... }
}

to JUnit 4-style tests:

class Test {
   @Test public void yadaYada() { ... }
}
Kjetil Ødegaard
Does that really help anything?
Mr. Shiny and New
Yeah, a summary of advantages would be nice. It is not obviously clear from the example why JUnit 4 style would be better.
Jonik
@Test(expected=ExpectedException.class)
toolkit
Your test case can now extend the class under test to easily test protected methods.
Mark Renouf
@Mark: For those using the common idiom of having test class *in the same package* as the code being tested (but under different dir structure), there's nothing new there.
Jonik
+9  A: 

Although I admit that static imports can easily be overused, I like to use

import static Math.* ;

in classes that use a lot of Math functions. It can really decrease the verbosity of your code. I wouldn't recommend it for lesser-known libraries, though, since that can lead to confusion.

Bill the Lizard
+1. Especially useful if you have a bunch of constants defined in one class, and another class heavily uses them. Of course, misusing static imports can easily make code less clear (especially for method calls).
Jonik
+8  A: 

The new for-each construct to iterate over arrays and collection are the biggest for me.

These days, when ever I see the boilerplate for loop to iterate over an array one-by-one using an index variable, it makes me want to scream:

// AGGHHH!!!
int[] array = new int[] {0, 1, 2, 3, 4};
for (int i = 0; i < array.length; i++)
{
    // Do something...
}

Replacing the above with the for construct introduced in Java 5:

// Nice and clean.    
int[] array = new int[] {0, 1, 2, 3, 4};
for (int n : array)
{
    // Do something...
}

Clean, concise, and best of all, it gives meaning to the code rather than showing how to do something.

Clearly, the code has meaning to iterate over the collection, rather than the old for loop saying how to iterate over an array.

Furthermore, as each element is processed independent of other elements, it may allow for future optimizations for parallel processing without having to make changes to the code. (Just speculation, of course.)

coobird
+11  A: 

Formatted printing was introduced as late as in JDK 1.5. So instead of using:

String str = "test " + intValue + " test " + doubleValue;

or the equivalent using a StringBuilder,

one can use

String str = String.format("test %d test %lg", intValue, doubleValue);

The latter is much more readable, both from the string concatenation and the string builder versions. Still I find that people adopt this style very slowly. Log4j framework for example, doesn't use this, although I believe it would be greatly benefited to do so.

kgiannakakis
I still don't get why they made format a static method. "test %d".format(intValue) would look much nicer!
Joachim Sauer
Don't see this used much, force of habit, most likely. For more modern logging you can look at logback, which is very nice, and backward compatible with log4j.
Steve B.
+1 on the log4j - I miss this feature that does exist in log4net.
ripper234
Another logging option is Simple Logging Facade for Java (SLF4J), it supports varargs.
pauxu
Jonik
+2  A: 

Converting classes to use generics, thereby avoiding situations with unnecessary casts.

Jen
+2  A: 

I'm a little wary to refactor along these lines if that is all you are doing to your source tree. The examples so far do not seem like reasons alone to change any working code base, but maybe if you are adding new functionality you should take advantage of all the new stuff.

At the end of the day, these example are not really removing boiler plate code, they are just using the more manageable constructs of newer JDKs to make nice looking boiler plate code.

Most ways to make your code elegant are not in the JDK.

+2  A: 

Okay, now it's my turn to get yelled at.

I don't recommend 90% of these changes.

It's not that it's not a good idea to use them with new code, but breaking into existing code to change a for loop to a for(:) loop is simply a waste of time and a chance to break something. (IIWDFWI) If it works, don't fix it!

If you are at a real development company, that change now becomes something to code-review, test and possibly debug.

If someone doing this kind of a refactor for no reason caused a problem of ANY sort, I'd give them no end of shit.

On the other hand, if you're in the code and changing stuff on that line anyway, feel free to clean it up.

Also, all the suggestions in the name of "Performance" really need to learn about the laws of optimization. In two words, Don't! Ever! (Google the "Rules of optimization if you don't believe me).

Bill K
If it works but is ugly and you don't fix it when you have the time, then chances are that when you need to make changes to the code you will be too busy to do the refactoring, too scared because a tight deadline is looming, and more likely to make errors because you didn't clean it up when you had the chance. The basic idea of refactoring is that it pays off technical debt and makes your life easier in the future.
Michael Borgwardt
What is ugly? I consider ugly duplicate code, nasty parameter sets on a method, classes that have too much intimate knowledge of each other, etc. A traditional for loop isn't really difficult or anything, just less concise. If it's actually fixing one of the "Ugly" items I just mentioned, go for it! But don't just start adding (or removing) this. in front of every member variable because you think one way or the other is less "Ugly"
Bill K
These refactorings shouldn't take any human input whatsoever to be implemented.
_ande_turner_
+4  A: 

Related to varargs; the utility method Arrays.asList() which, starting from Java 5, takes varargs parameters is immensely useful.

I often find myself simplifying something like

List<String> items = new ArrayList<String>();
items.add("one");
items.add("two");
items.add("three");
handleItems(items);

by using

handleItems(Arrays.asList("one", "two", "three"));
Jonik
+21  A: 

reading a string from standard input:

Java pre-5:

try {
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    String str = reader.readLine();
    reader.close();
}
catch (IOException e) {
    System.err.println("error when closing input stream.");
}

Java 5:

Scanner reader = new Scanner(System.in);
String str = reader.nextLine();
reader.close();

Java 6:

Console reader = System.console();
String str = reader.readLine();
cd1
Unbelievable that it took them so long to make such simple thing really simple to code!
zvrba
String string = System.console().readLine(); is even more minimal
_ande_turner_
this one is significant. ;-)
djangofan
+2  A: 

Using Swing's new DefaultRowSorter to sort tables versus rolling your own from scratch.

xeon
A: 

It is worth noting that Java 5.0 has been out for five years now and there have only been minor changes since then. You would have to be working on very old code to be still refactoring it.

Peter Lawrey
+7  A: 

copying an existing array to a new array:

pre-Java 5:

int[] src = new int[] {1, 2, 3, 4, 5};
int[] dest = new int[src.length];
System.arraycopy(src, 0, dest, 0, src.length);

Java 6:

int[] src = new int[] {1, 2, 3, 4, 5};
int[] dest = Arrays.copyOf(src, src.length);

formerly, I had to explicitly create a new array and then copy the source elements to the new array (calling a method with a lot of parameters). now, the syntax is cleaner and the new array is returned from a method, I don't have to create it. by the way, the method Arrays.copyOf has a variation called Arrays.copyOfRange, which copies a specific region of the source array (pretty much like System.arraycopy).

cd1
interesting. thanks.
djangofan
Old post, I know, but if you want to make an exact copy of an array then `clone()` is the easiest way (and has been from the birth Java): `int[] copy = original.clone();` The `clone` return type for arrays is covariant (since 1.5), so you don't even need casting. But I agree `Arrays.copyOf` is sweet, sweet sugar for other copy operations.
gustafc
+3  A: 

Annotations

I wonder no one mentioned it so far, but many frameworks rely on annotations, for example Spring and Hibernate. It is common today to deprecate xml configuration files are in favor of annotations in code (though this means losing flexibility in moving from configuration to meta-code, but is often the right choice).The best example is EJB 2 (and older) compared to EJB 3.0 and how programming EJB has been simplified thanks to annotations.

I find annotations also very useful in combination with some AOP tools like AspectJ or Spring AOP. Such combination can be very powerful.

Danijel Arsenovski
+1  A: 

Improved singleton patterns. Technically these are covered under the popular answer enums, but it's a significant subcategory.

public enum Singleton {
    INSTANCE;

    public void someMethod() {
        ...
    }
}

is cleaner and safer than

public class Singleton {
    public static final Singleton INSTANCE = new Singleton();

    private Singleton() {
        ...
    }

    public void someMethod() {
        ...
    }
}
Lord Torgamus