2017-01-25 2 views
2

Я пытаюсь преобразовать класс Java для Swift:Новый Свифта - failable инициализаторах

// 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, инициализация завершается неудачно, и в терминале печатается заявление, объясняющее, что значение не было в допустимом диапазоне. Я попытался воссоздать это в Свифт.

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

Вышеуказанные работы в соответствующих случаях, но, как представляется, по-прежнему создает экземпляр карты, даже если передаются неприемлемое значение, потому что я все еще могу использовать setNumValue (значение :) метод для изменения значения.

Я попытался сделать инициализацию (метод numvalue :) неудачной, но затем я получу опцию (Карту), если инициализация прошла успешно. В идеале я бы хотел, чтобы любой успешно инициализированный объект Карты был объектом Карты, а не Факультативным, содержащим Карту. Это возможно?

+2

Ваш класс Java Безразлично 't fai l для инициализации, если вы вводите недопустимый номер, он просто не устанавливает 'numvalue'. Вы все равно получите экземпляр «Card» от конструктора. В Swift вы захотите условно развернуть * по желанию, вы вернетесь из неисправного инициализатора, чтобы узнать, успешно ли это или нет. Я настоятельно рекомендую прочитать раздел [optionsals руководства по языку] (https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-ID330), который охватывает все это очень подробно. – Hamish

+2

@LouisDeVesto Добавить комментарий Хамиша: конструкторы Java не имеют эквивалентной функции для инициализаторов Swift. Вместо этого в Java люди будут использовать статические методы, которые вызывают для вас конструктор, который либо возвращает новый объект, либо «null». – Alexander

+0

Также btw, вы можете проверить, является ли значение в диапазоне более просто: 'if 1 ... 10 ~ = numValue {...' – Alexander

ответ

2

Ну, вы должны проверить возвращаемые Дополнительно ли это ноль или нет, а затем разворачивать его в неопциональный:

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> 
} 
+3

Класс, подобный этому, действительно будет лучше, чем структура. Существует много ненужных накладных расходов, учитывая, что основная функция объекта - хранить одно значение «Int». –

+1

Полностью согласен, но, возможно, есть еще вопрос, и он просто сводится к простому образцу кода. Никогда не знаешь... –

0

В идеале я хотел бы, чтобы любой успешно инициализированный объект карты является объектом карты, а не опционным, содержащим карту. Это возможно?

Это невозможно при стандартных схемах инициализации, доступных в Swift. В конечном итоге вы получите Optional<Card>, если при определенных условиях ваша инициализация завершится неудачно.


Failable Initialization

Вы можете сделать свой инициализатора failable, добавив ? к концу init ключевого слова. То есть init?.

Это означает, что инициализатор может выйти из строя (вернуть нуль).

В вашем инициализаторе вы можете вернуть нуль, когда условия для создания экземпляра не выполняются.

В этом случае я использую оператор 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! 
    } 
} 

Поскольку вы теперь возвращаетесь дополнительный экземпляр Card, вы можете использовать if let безопасно разворачивать по желанию и сделать что-то с этим экземпляром, или обрабатывать тот факт, что экземпляр не был создан успешно (у вас есть ноль).

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

В приведенном выше примере, «Невозможно создать экземпляр карты» строка будет напечатана (в дополнение к «значение карты должно быть между 0 и 11»).

Дополнительная информация о том, как работает сбой инициализации в Swift, можно найти here.

1

Если ваш тип объекта не требуется, вы должны использовать Struct.BTW не нужно делать значение value переменной и создавать методы, чтобы просто изменить его свойства. Вы просто создаете новый объект Карты, чтобы заменить его вместо того, чтобы изменять старый.

Ваша структура карта должна выглядеть следующим образом:

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

Я хотел бы, чтобы любой успешно инициализирован объект карты быть карты объект, а не опционально, содержащий карты. Это возможно?

Да, это возможно. Просто добавьте предпосылку к вашему инициализатору и сделать его ноны failable:

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" 
Смежные вопросы