假设我在Bookshelf类中有一个方法getBook()
。如果Bookshelf不为空,则该方法应仅返回Book对象。但是,Java要求该方法在所有情况下都返回Book对象。我可以有一个首先调用的方法isShelfEmpty()
,但是向调用者展示Bookshelf的实现似乎很尴尬。最干净的方法是什么?
Iterator
会使这更简单,但是这是针对一个类项目的,我们还没有在类中讨论过迭代器。
修改
我考虑过返回null,但出于某种原因,这对我来说似乎有些难看。这是最好的选择,还是有其他选择?
答案 0 :(得分:10)
如果没有可用的书,您可以返回null
。然后,当然,每次你要书时都需要检查null
。
我会实现一个方法isBookAvailable()
并在没有可用的书时调用方法getBook()
时抛出异常。
答案 1 :(得分:4)
正如tangens所说,你可以返回null
或抛出异常。您选择哪个选项取决于是否从空Bookshelf
请求图书是错误。
理想情况下,Bookshelf
将保留对Book
个实例集合的引用,如果需要,它们可以为空。这对您的模型来说是一个更好的抽象。
答案 2 :(得分:1)
您有两个基于非迭代器的选项。
Book getBook(...)
将不会返回任何内容。在这种情况下,代码看起来像:
public class BookShelf {
Book getBook(...) {
if (empty) {
return null;
}
}
}
Book
表示没有其他图书。如果货架是空的,您将返回哨兵。通常,Sentinel使用NIL约定命名,该约定类似于null
但是是完整对象。所以在你的书课上你会有类似的东西:
public class BookShelf {
public static final NIL = new Book();
...
public Book getBook(...) {
if (empty) {
return NIL;
}
}
}
这避免了使用空指针;但是,它涉及使用额外的“NIL”对象。在Java中,节省的费用很少,但有时它们是值得的。基本上第一个解决方案的代码看起来像
if (shelf.getBook(...) == null) {
System.out.println("We ran out of books!");
}
而第二个解决方案中的代码看起来像
if (shelf.getBook(...) == BookShelf.NIL) {
System.out.println("We ran out of books!");
}
除非是Java,否则对第二个选项的更好答案是
if (BookShelf.NIL.equals(shelf.getBook(...))) {
System.out.println("We ran out of books!");
}
所以你看,有很多方法可以解决这个问题,但每种方法都有不同的优点和缺点。关键是要么你没有返回任何东西(并编写你的代码来处理没有被返回的东西),要么你返回的东西意味着“书架上没有书籍”。
答案 3 :(得分:1)
如果没有可用的书籍,则应抛出java.lang.IllegalStateException。从javadocs,一个IllegalStateException
表示在非法或不适当的时间调用了某个方法。换句话说,Java环境或Java应用程序未处于所请求操作的适当状态。
所以我希望代码看起来像:
public Book getBook() {
if(books.isEmpty()) {
throw new IllegalStateException();
}
// return the book, knowing that the books list has at least one item in it.
}
答案 4 :(得分:0)
如果您不想返回Null,那么您需要返回书籍或书籍。要实现这一点,您需要一个类似于“BookOrEmptySpace”(愚蠢名称)的父类或接口,它可以有子项“Book”或“EmptySpace”。
您正在讨论的方法会返回BookOrEmptySpace,然后您进行测试以查看它是Book的实例还是EmptySpace的实例。
除非EmptySpace拥有自己的某些属性,否则这不会添加任何内容,因此几乎所有人都只返回null - 实际上,null正是您的情况所需要的,并且是大多数程序员本质上直观的案例之一。
你可能希望用注释标记它 - 在这种情况下我认为它是@Nullable(这表明大多数程序员如果没有书要返回它将返回null)
答案 5 :(得分:0)
如果没有任何东西,你怎么知道什么都没有?!?!最后你必须进行测试,看看是否有东西 - 这可以在你尝试获得某些东西之前完成,也可以在你获得之后完成,然后检查你得到的东西是否是一本书。
老实说,你希望任何编程语言在这种情况下做什么?!?!
答案 6 :(得分:0)
两种不同的做法。
主要使用C / C ++和Python进行编程我可以告诉您,对于没有任何可用且没有指定具体标识符的情况(例如getBookAtIndex(14)
),约定是返回null,NULL,None等。抛出异常保留用于您希望存在具有严格标识符的书但不存在的情况。
答案 7 :(得分:0)
实际上,我看到了许多可行的选择,每个选项都有自己的优点/缺点。
如Edwin Buck所描述的那样,在没有书籍时返回一个哨兵对象:
public Book getBook() {
public static Book NO_BOOK = new Book();
if(books.empty()) {
return NO_BOOK;
}
...
}
book = shelf.getBook();
if(Book.NO_BOOK.equals(book)) {
// there were no books
}
如果没有更多书籍,则返回null,如您所说,您不想这样做
public Book getBook() {
if(books.empty()) {
return null;
}
...
}
book = shelf.getBook();
if(book == null) {
// there were no books
}
当没有书籍时抛出异常,并实现hasMoreBooks。
public boolean hasMoreBooks() {
return (! books.empty());
}
public Book getBook() {
if(books.empty()) {
throw new IllegalStateException("no more books");
}
...
}
if(shelf.hasMoreBooks()) {
book = shelf.getBook();
// you know it's a valid book here
}
在其他语言中按照Haskell实现Maybe类型包装。这种类型的对象表示“可能是Book,或者可能是Nothing”。然后,呼叫者负责检查它是否是有效的书。对于为这种类型的操作提供良好支持的语言(模式匹配等)并且不支持null(null是teh devil;),这是一个很好的解决方案。对于Java,我倾向于认为它太过分了。
至于“揭露实施”,我认为用户不知道书架可能“没有书籍”是不合理的,如果是这样的话就不可能从中获取书籍。
修改:我会添加另一个与此问题没有直接关系的备注,但值得一提。在没有书籍(以及实施hasMoreBooks)上抛出异常的好处之一是你不“总是”需要检查是否有书。
例如,如果你有一个紧密的循环遍历书籍并且有一本书FAR比没有书更频繁,你可以做如下的事情:
try {
while(true) {
book = shelf.getBook();
...
}
} catch(IllegalStateException ex) {
// no more books
}
公平地说,在大多数情况下并不是那么有用......但是如果你担心必须打电话给hasMoreBooks的性能成本,而且你知道你很少有更多的书,上面可能是方便考虑。