Reputation: 1578
I'm trying to clean up some legacy code and have some unchecked cast
warnings I'm struggeling to get rid of.
I've extracted the code giving the warnings into a compilable program below. Note that I removed much of the code to make it smaller so all of it might not make complete sense. It compiles, but running it wont do anything.
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;
public class GenericsTest {
public static void main(String[] args) throws IOException {
Reader reader = new Reader();
List<String> stringEntries = reader.readAll(StringEntry.class);
}
public static class Reader {
public <T> ZipInputStream getInputStream(String fileName) throws ZipException, FileNotFoundException {
return new ZipInputStream(new FileInputStream(fileName));//_file.getInputStream(_paths.get(fileName));
}
public <T, TEntry extends Entry<T>> List<T> readAll(Class<TEntry> type) throws IOException {
List<T> list = new ArrayList<T>();
List<TEntry> entries = createEntries(type);
for (TEntry entry : entries) {
list.add(read(entry));
}
return list;
}
public <T> T read(Entry<T> entry) throws IOException {
ZipInputStream is = null;
try {
//is = _archive.getInputStream(entry.getName());
return entry.read(is);
} finally {
if (is != null) {
is.close();
}
}
}
public <TEntry extends Entry> List<TEntry> createEntries(Class<TEntry> type) throws ZipException {
List<TEntry> entries = new ArrayList<TEntry>();
List<String> paths = new ArrayList<String>();//getPaths(type);
for (String path : paths) {
entries.add(createEntry(type, path));
}
return entries;
}
public <TEntry extends Entry> TEntry createEntry(Class<TEntry> type, String folder) {
if (StringEntry.class.equals(type)) {
return (TEntry) new StringEntry(folder);
} else if (IntegerEntry.class.equals(type)) {
return (TEntry) new IntegerEntry(folder);
}
throw new IllegalArgumentException("Unknown type: " + type);
}
}
public static abstract class Entry<T> extends ZipEntry {
private T _data;
public Entry(T data, String folder, String name) {
super(folder + "/" + name);
_data = data;
}
protected abstract T read(InputStream is) throws IOException;
};
public static class StringEntry extends Entry<String> {
public StringEntry(String folder) {
super("Hallo world!", folder, "StringEntry");
}
@Override
protected String read(InputStream is) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
};
public static class IntegerEntry extends Entry<Integer> {
public IntegerEntry(String folder) {
super(42, folder, "IntegerEntry");
}
@Override
protected Integer read(InputStream is) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
};
}
Compiling the above code gives the following warnings
GenericsTest.java:57: warning: [unchecked] unchecked cast
found: GenericsTest.StringEntry
required: TEntry
return (TEntry) new StringEntry(folder);
GenericsTest.java:59: warning: [unchecked] unchecked cast
found: GenericsTest.IntegerEntry
required: TEntry
return (TEntry) new IntegerEntry(folder);
2 warnings
Changing
public <TEntry extends Entry> TEntry createEntry...
to
public <TEntry extends Entry<T>> TEntry createEntry...
gives a compiler error (cannot find symbol: class T
).
I don't want to change the code too much since it is working fine, so how can I fix (not hide) the warnings with the least code changes?
Upvotes: 1
Views: 1373
Reputation: 39950
You're already passing the Class
object into the method, just use Class.cast()
:
public <TEntry extends Entry<?>> TEntry createEntry(Class<TEntry> type, String folder) {
if (StringEntry.class.equals(type)) {
return type.cast(new StringEntry(folder));
} else if (IntegerEntry.class.equals(type)) {
return type.cast(new IntegerEntry(folder));
}
throw new IllegalArgumentException("Unknown type: " + type);
}
The above change makes your code compile without warnings for me.
Also, the <T>
in the signature of getInputStream()
is probably not necessary.
Upvotes: 2
Reputation: 22514
If you want to use more than one generic parameter you will need to specify it in the parameter list. Can you try this?
public <T, TEntry extends Entry<T>> TEntry createEntry(...
UPDATE
I had some time to play with this and put those nice class objects in use:
public <T, TEntry extends Entry<T>> List<TEntry> createEntries(Class<TEntry> type) throws ZipException {
List<TEntry> entries = new ArrayList<TEntry>();
List<String> paths = new ArrayList<String>();//getPaths(type);
for (String path : paths) {
entries.add(createEntry(type, path));
}
return entries;
}
public <T, TEntry extends Entry<T>> TEntry createEntry(Class<TEntry> type, String folder) {
if (StringEntry.class.equals(type)) {
return type.cast(new StringEntry(folder));
} else if (IntegerEntry.class.equals(type)) {
return type.cast(new IntegerEntry(folder));
}
throw new IllegalArgumentException("Unknown type: " + type);
}
}
Upvotes: 2
Reputation: 1588
IMHO for legacy code, to make it generic friendly can be quite an experience, specially if you have a large base of code... Since probably this code has been deployed, would you not consider an iterative approach?
By iterative approach I mean... Get all your warning and eliminate them with @SuppressWarning, and with a TODO annotation then, as the time goes by, every time you need to do a change in your code, if you stumble upon a TODO, just change it then...
I am just saying in case that you are just migrating to Java 5 and have thousands of warnings..
Upvotes: 0