泛型和未经检查的转换

Generics and unchecked cast

我知道这个问题被问了很多次,但无论如何我找不到可以应用于我的代码的东西。也许是因为我没有完全理解一切。我只想避免 @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 方法可以 return Book 或 SearchableBook 避免使用两个非常相似的方法。

我尝试将类型作为参数发送给执行类似操作的方法,但我仍然收到相同的警告。我仍然需要 return 通用类型,所以我肯定有同样的问题 :

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 的资源发生了什么?我说的是特定于可搜索书籍(MyBook 包装器)的搜索功能 => findSentence() 的实现所使用的资源。它们是否仅在我的 OpenBook 方法中使用并且 GC 之后清除它们? .其实它看起来很简单,我不明白为什么我没有早点考虑...

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

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

如果你这样定义会怎么样:

public static <T extends Book> T openBook(String ebookFilePath, boolean searchable)

更新:

创建一个包含 Book 作为字段的包装器 MyBook。然后创建一个接口Searchable并在MyBook中实现它。然后你可以删除通用的:

public static MyBook openBook(String ebookFilePath, boolean searchable)

为字段 Book 创建 getter 和 setter。

您是否考虑过创建接口而不是使用泛型?这样,您的 Book 或 SearchableBook 都可以实现它,并且您的转换不会有问题。

编辑:

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

我终于改变了我的架构。

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

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 class,但后来意识到我无法将 Book 转换为 SearchableBook,这对我的计划来说是个问题。所以我尝试了我在本主题中解释的另一种方法。创建一个不扩展 Book 但将 Book 作为 属性 和 BookHelper class 的 SearchableBook class 以避免复制:

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 class 我尝试(如我的问题中所述)在我的 BookHelper class 中执行此操作:

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

但是遇到了未经检查的转换问题。

然后我尝试使用 Interface :

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

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

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

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

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