Swift 的新手 - 可失败的初始值设定项

New to Swift - failable initializers

我正在尝试将 Java class 转换为 Swift:

// Card class with only a main function
public class Card {
// fields
private int numvalue;

// constructor(s)
public Card(int v) {
    if (v > 0 && v < 11) {
        this.numvalue = v;}
    else {
        System.out.println("Error: Card value is outside the allowed range.
        Value must be between 1 and 10 inclusive.")
    }

如果我尝试使用值不在 1 到 10 之间的构造函数来初始化 Card 对象,则初始化失败,并在终端中打印一条声明,说明该值不在可接受的范围内.我试图在 Swift.

中重新创建它
class Card {
    var numValue : Int?
    init(numValue:Int) {
        if (numValue > 0 && numValue < 11) {self.numValue = numValue}
        else {print("Card value must be between 0 and 11")}
    }
    func setNumValue(value:Int) -> () {
        self.numValue = value
    }
    func getNumValue() -> Int {
        return self.numValue!
    }
}

以上在适当的情况下有效,但即使传递了不可接受的值,它似乎仍会创建一个 Card 实例,因为我仍然可以使用 setNumValue(value:) 方法来更改值。

我尝试使 init(numvalue:) 方法失败,但如果初始化成功,我会得到一个 Optional(Card)。理想情况下,我希望任何成功初始化的 Card 对象都是 Card 对象,而不是包含 Card 的 Optional。这可能吗?

嗯,你必须检查返回的 Optional 是否为 nil,然后将其解包成 non-Optional:

class Card {
    var numValue : Int
    init?(numValue:Int) {
        guard (numValue > 0 && numValue < 11) else {
            print("Card value must be between 0 and 11")
            return nil
        }

        self.numValue = numValue
    }
    func setNumValue(value:Int) -> () {
        guard (numValue > 0 && numValue < 11) else {
            print("Card value must be between 0 and 11")
            return
        }
        self.numValue = value
    }
    func getNumValue() -> Int {
        return self.numValue
    }
}


let cOk = Card(numValue:1)
let cFails = Card(numValue:-1)

if let c = cOk {
    // now work with c because it's a Card,
    // not an Optional<Card> 
}

Ideally, I'd like for any successfully initialized Card object to be a Card object, not an Optional containing a Card. Is this possible?

Swift 中可用的标准初始化方案无法做到这一点。如果您的初始化在某些情况下失败,您最终将得到 Optional<Card>


初始化失败

您可以通过在 init 关键字的末尾添加 ? 来使您的初始化程序可失败。即init?.

这表明初始化程序可能会失败 (return nil)。

在您的初始化程序中,当不满足创建实例的条件时,您可以 return nil。

在本例中,我使用的是 guard 语句,但您也可以使用 if 语句。

class Card {
    var numValue : Int?

    init?(numValue: Int) {
        guard numValue > 0 && numValue < 11 else {
            print("Card value must be between 0 and 11")
            return nil
        }
        self.numValue = numValue
    }

    func setNumValue(value:Int) -> () {
        self.numValue = value
    }

    func getNumValue() -> Int {
        return self.numValue!
    }
}

由于您现在正在 returning 一个 Card 的可选实例,您可以使用 if let 安全地解包可选并对实例做一些事情,或者处理这样一个事实一个实例没有成功创建(你有一个 nil)。

let card = Card(numValue: 12)
if let card = card {
    print(card)
} else {
    print("Unable to create a Card instance")
}

在上面的示例中,将打印 "Unable to create a Card instance" 字符串(除了 "Card value must be between 0 and 11")。

有关 Swift 中可失败初始化如何工作的更多信息,请参见 here

如果您的对象类型不需要持久化,您应该使用 Struct。顺便说一句,无需将值 属性 设为变量并创建方法来仅更改其属性。您只需创建一个新的 Card 对象来替换它而不是更改旧的。

您的 Card 结构应该如下所示:

struct Card {
    let value: Int
    init?(value: Int) {
        guard 1...10 ~= value else {
            print("Card value is out of range 1...10")
            return nil
        }
        self.value = value
    }
}

let card = Card(value: 2)       // returns an object of optional type (Card?)   "optional Card(value: 10)\n"
let nilCard = Card(value: 11)   // valus out of the range 1...10 will return nil

if let card = Card(value: 10)  {
    print(card)   // "Card(value: 10)\n"
}

I'd like for any successfully initialized Card object to be a Card object, not an Optional containing a Card. Is this possible?

是的,这是可能的。只需向您的初始化程序添加一个前提条件并使其不可失败:

struct Card {
    let value: Int
    init(value: Int) {
        precondition(1...10 ~= value, "Card value is out of range 1...10")
        self.value = value
    }
}


let card = Card(value: 2)                // returns an object of type (Card)   "Card(value: 2)\n"
let cantInitCard = Card(value: 11)       // won't execute (precondition not met range 1...10)

您还可以为您的卡片初始值设定项添加第二个参数范围(可选,因为您可以设置默认值)并向您的卡片添加一个范围 属性,这样您就可以检查您可能的卡片值是什么:

struct Card {
    let value: Int
    let range: ClosedRange<Int>
    init(value: Int, range: ClosedRange<Int> = 1...10) {     // you can set a default range for yor cards
        precondition(range ~= value, "Card value is out of range: " + range.description)
        self.value = value
        self.range = range
    }
}

let card = Card(value: 5)       // returns an object of type (Card) "Card(value: 5, range: 1...10)\n"
print(card.value, card.range)   // "5 1...10\n"

如果您想要一张具有不同范围 1...13 值的卡片,只需传递范围

let newCard = Card(value: 11,  range: 1...13)    // ok to execute (precondition met range 1...13) 
print(newCard.value, newCard.range)              // "11 1...13\n"