【问题标题】:Generics and unchecked cast泛型和未经检查的演员表
【发布时间】:2017-01-17 11:34:47
【问题描述】:

我知道这个问题被问了很多时间,但无论如何我找不到可以应用于我的代码的东西。也许是因为我没有完全理解一切。我只是想避免@SuppressWarnings("unchecked") 解决方案。所以为我的代码寻找一个新的架构。

当我尝试转换为泛型类型时,我在这一行收到警告 Unchecked cast:com.example.reader.models.SearchableBook to T(T)epubReader.readEpub(fileInputStream)

public class BookHelper {
   public static <T> T openBook(String ebookFilePath, boolean searchable) {
        T book = null;
        EpubReader epubReader = new EpubReader();
        FileInputStream fileInputStream = null;

        try {
            fileInputStream = new FileInputStream(ebookFilePath);
            book = searchable ? (T)new SearchableBook(epubReader.readEpub(fileInputStream)) : (T)epubReader.readEpub(fileInputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return book;
    }
}

我使用泛型是因为我希望 BookHelper 方法可以返回 Book 或 SearchableBook,避免使用两个非常相似的方法。

我尝试将类型作为参数发送给执行类似操作的方法,但我仍然收到相同的警告。我仍然必须返回一个泛型类型,所以我肯定有同样的问题:

public static <T> T openBook(String ebookFilePath, boolean searchable, Type t) {
    T book = null;
    EpubReader epubReader = new EpubReader();
    FileInputStream fileInputStream = null;

    try {
        fileInputStream = new FileInputStream(ebookFilePath);
        if(t instanceof Book) {
            book = (T)epubReader.readEpub(fileInputStream);
        } else ...

在最后一种情况下,它根本不是不安全的,所以我可以使用 SuppressWarnings 但我想以正确的方式这样做......我想知道人们在这种情况下通常是如何做的。我想最好的方法是不要复制我的代码来避免这个警告......

更新 1

现在我有了我的可搜索界面:

public interface Searchable {

    String findSentence(String words);
}

实现此接口的包装器 MyBook:

public class MyBook implements Searchable  {
    private Book mBook;

    public MyBook(Book book) {
        this.setBook(book);
    }

    public Book getBook() {
        return mBook;
    }

    public void setBook(Book book) {
        mBook = book;
    }

    @Override
    public String findSentence(String words) {
        return null;
    }
}

我的openBook(String ebookFilePath, boolean searchable) 方法:

public static MyBook openBook(String ebookFilePath, boolean searchable) {
    MyBook book = null;
    EpubReader epubReader = new EpubReader();
    FileInputStream fileInputStream = null;

    try {
        fileInputStream = new FileInputStream(ebookFilePath);
        book = new MyBook(epubReader.readEpub(fileInputStream));

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return book;
}

在 MainActivity 中:

protected void onCreate(Bundle savedInstanceState) {  
...  
        MyBook searchBook = BookHelper.openBook(BOOK0_PATH, true);
        Book book = BookHelper.openBook(BOOK1_PATH,false).getBook();
...    
}

它可以工作,但没有代码复制。如果我想实例化一本书,我只需实例化包装器并调用 getter getBook()。但是,用于实例化 MyBook 而我不需要 Book 的资源会怎样呢?我说的是搜索功能的实现所使用的资源 => findSentence() 特定于可搜索的书(MyBook 包装器)。它们是否仅在我的OpenBook 方法中使用,GC 之后会清除它们吗? .其实它看起来很简单,我不明白为什么我没有早点想到它......

但是,界面的目标是什么?因为显然我并不真正需要它,因为 MyBook 类中有几个方法,没有一个是强制性的,所以不需要放入接口。希望我很清楚。

【问题讨论】:

    标签: android generics casting warnings


    【解决方案1】:

    如果你定义这样的东西会怎样:

    public static &lt;T extends Book&gt; T openBook(String ebookFilePath, boolean searchable)

    更新:

    创建一个包装器MyBook,其中包含Book 作为一个字段。然后创建一个接口Searchable并在MyBook中实现。然后你可以删除泛型:

    public static MyBook openBook(String ebookFilePath, boolean searchable)

    为字段Book 创建getter 和setter。

    【讨论】:

    • 所以我的泛型类型将继承自 Book。这应该如何工作?如果 T 扩展 Book,我不能将 SearchableBook 转换为 T。
    • 好吧,我认为 SearchableBook 扩展了 Book... 如果不是,我建议使用接口而不是泛型类型。
    • 好的,所以如果我使用 public static &lt;T extends Book&gt; T openBook(String ebookFilePath, boolean searchable)SearchableBook extends Book 应该可以吗?
    • 它不起作用。它说我required T found Book,即使是public static &lt;T extends Book&gt; T openBook(String ebookFilePath, boolean searchable)。如果我投到 T 我有警告。
    • 如果我使用一个接口,那么我必须使用 Ebook 和 SearchableBook 的不同实现来实现代码两次。我无法修改电子书,它来自外部图书馆。或者,也许我不明白什么。
    【解决方案2】:

    使其成为检查异常:void f() throws IllegalArgumentExceprion;

    【讨论】:

    • public static &lt;T&gt; T openBook(String ebookFilePath, boolean searchable) throws IllegalArgumentException { ... } 仍然有警告。
    【解决方案3】:

    我终于改变了我的架构。

    我想使用 Epublib 中的 Ebook 类,并添加在书中搜索以查找包含特定单词的句子的功能,这就是我的 SearchableBook 类的目的。

    import nl.siegmann.epublib.domain.Book;
    
    public class SearchableBook {
        private Book mBook;
    
        // Find a sentence containing the words in parameters
        public Sentence findSentence(String[] wordsToFind) { ... }
    
        ...
    }
    

    首先,我尝试扩展 Epublib Book 类,但后来意识到我无法将 Book 转换为 SearchableBook,这对我的计划来说是个问题。所以我尝试了我在本主题中解释的另一种方法。创建一个 SearchableBook 类,它不扩展 Book,但有一个 Book 作为属性和一个 BookHelper 类以避免重复:

    EpubReader epubReader = new EpubReader();
    FileInputStream fileInputStream = null;
    
    try {
        fileInputStream = new FileInputStream(ebookFilePath);
        book = epubReader.readEpub(fileInputStream);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

    为了能够将此代码用于 BookSearchableBook 类,我尝试(如我的问题中所述)在我的 BookHelper 类中执行此操作:

     public static <T> T openBook(String ebookFilePath, boolean searchable) throws IllegalArgumentException {
            T book = null;
            EpubReader epubReader = new EpubReader();
            FileInputStream fileInputStream = null;
    
            try {
                fileInputStream = new FileInputStream(ebookFilePath);
                book = searchable ? (T)new SearchableBook(epubReader.readEpub(fileInputStream)) : (T)epubReader.readEpub(fileInputStream);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return book;
        }
    

    但是遇到了 Unchecked cast 问题。

    然后我尝试使用Interface

    public interface BookBehaviour { Object read(String ebookFilePath); }
    

    但是后来我不得不在我的SearchableBook 类中实现这个读取方法,需要为我不可搜索的书添加另一个类才能在其中实现读取方法......所以我回到了我的第一个问题,复制。

    最后我回到了一个更简单的方法。不再有继承、接口、泛型或反射......只是创建了一个BookHelper 类,它接收Book 作为参数并实现搜索功能。然后我可以实例化两本书,一本将使用我的BookHelper 类,另一本不使用。这个 BookHelper 类也将包含一个方法来打开这本书并避免 InputStream 代码的复制。

    我只是想使用这些更复杂的东西,因为我看到每个人都在他们的项目中使用......但我意识到我应该继续这样做,因为即使我开始理解这些概念并阅读大量关于它的内容,我也从来不需要这些东西在我的项目中,就像在这个特定的项目中一样。

    无论如何,如果您有更好的建议,那么欢迎您对这个问题有更好的答案。

    【讨论】:

      【解决方案4】:

      您是否考虑过创建接口而不是使用泛型?这样一来,您的 Book 或 SearchableBook 都可以实现它,并且您的演员表不会成为问题。

      编辑:

      实际上使用泛型根本没有逻辑,您可以将泛型更改为这样的接口:public interface BookBehaviour { Object readEpub(FileInputStream fileInputStream); } 然后您的类 SearchableBook 应该实现 BookBehaviour 并且您的方法 readEpub(fileInputStream) 应该返回 BookBehaviour 类型。

      【讨论】:

      • 所以?如果我理解得很好,那么我的 Book 和 SearchableBook 类实现了 BookBehaviour。然后我必须为每个类实现 readEpub() 方法,然后我的代码仍然被复制,不是吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-22
      • 1970-01-01
      相关资源
      最近更新 更多