简体   繁体   中英

Difficult unchecked cast warnings

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?

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);
    }
}

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.

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..

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM