Eric Wilson
Eric Wilson

Reputation: 59355

Why is this generic method unsafe?

The following method generates a warning, but it looks safe to me. I'm sure the problem is with me:

public <S extends CharSequence> S foo(S s) {
    return (S) new StringBuilder(s);
}

It looks like this will always return the argument s. Can anyone show an example that would cause this method to throw an exception?

Edit: I'm not particularly interested in the question of whether generics are necessary here. Rather, I'm looking for a demonstration of how this method is unsafe.

Upvotes: 2

Views: 668

Answers (6)

Erwin Smout
Erwin Smout

Reputation:

The return type of your method code is ALWAYS a StringBuilder.

That is because the declared type of the expression 'new StringBuilder(x)' is ALWAYS a StringBuilder, whatever the X.

It is TOTALLY pointless to try and cast this to anything. The "S" information is part of the erasure, which exists only at compile-time and is erased by the time the program runs, that is, run-time. (Casting is a run-time thing exclusively, and casting something to some type/class whose identity has been erased a run-time, is indeed totally pointless.)

Upvotes: 1

Dirk
Dirk

Reputation: 31053

The unsafety here lies not within the method itself (though it has its problems, too) but at the call site. The use of S for the input argument's type as well as for the return value tells the compiler, that whatever the type of object may be that is passed to the function, the result has the same type (or a derived type, actually).

Thus, the compiler is allowed to assume, that in the call

foo("hello, world")

the result will be a java.lang.String, while in the call

foo(new StringBuffer("hello, world"))

the result will be a StringBuffer, and so on. In both cases, however, your method does not return what it was supposed to return, namely, an object of the same type as the input argument. Instead, a StringBuilder is returned.

Actually, the only kind of input argument your method will work with is a StringBuilder, anything else will be doomed to crash with a ClassCastException sooner or later, as the compiler might (and often does) insert (hidden) casts at the call sites.

And of course, as others have already pointed out, the use of generics is not really necessary here, anyway.

Upvotes: 1

dfa
dfa

Reputation: 116334

For example this will compile:

String a = foo("ss");

but it will fail at runtime:

ClassCastException: java.lang.StringBuilder cannot be cast to java.lang.String

since foo returns S, the type of your input parameter (String in this case).

I think that you don't need to use generics here (as skaffman said in his answer):

public StringBuilder foo(CharSequence s) {
    return new StringBuilder(s);
}

Upvotes: 2

skaffman
skaffman

Reputation: 403471

It's unsafe because while StringBuilder is a CharSequence, it isn't necessarily of type S (in fact, it almost certainly won't be). You can see this failing just by passing a String into your method. This will construct a StringBuilder using your String as an argument, and then try to cast your StringBuilder to String (which will fail).

There's probably no need to use generics here at all, this should work fine:

public CharSequence foo(CharSequence s) {
    return new StringBuilder(s);
}

Upvotes: 6

nraynaud
nraynaud

Reputation: 5122

foo("test");

is enough to make java try to cast a StringBuilder in a String.

Your code is guaranteed o be wrong, can you explain what you're trying to achieve plase ?

Upvotes: 1

Steven A. Lowe
Steven A. Lowe

Reputation: 61233

my Java is rusty, but would this method not throw whatever exceptions

new StringBuilder(s)

can throw?

Upvotes: 0

Related Questions