how can I modify the property of a class in its subclasses? - swift

class Animals{
var name : String = "default"
var age : Int = 0
func Details()-> String{
return "This animal is a \(name) and has \(age) years old."
}
}
class Dogs : Animals{
name = "dog"
}
class Cats : Animals{
name = "cat"
}
var MyAnimal = Dogs()
I want to see this message : "This animal is a dog and has 0 years old."
But each time I receive this one : "This animal is a default and has 0 years old."
var HisAnimal = Cats()

If you want stored, not computed, properties, you can set name in the initializer, like this:
class Animal {
let name: String
var age: Int = 0
/* designated initializer: fully initializes all instance properties */
init(name: String) {
self.name = name
}
func details() -> String {
return "This animal is a \(name) and has \(age) years old."
}
}
class Dog : Animal {
/* designated initializer of subclass: must call a designated
initializer from its immediate superclass */
init() {
super.init(name: "dog")
}
}
class Cat : Animal {
/* ... */
init() {
super.init(name: "cat")
}
}
let myAnimal = Dog()
This mechanism ensures that name is set from only one place, and is passed to the initializer explicitly.

Alternatively using protocols (and generics) over classes
You will most likely create instances of Cat:s and Dog:s, but you might not want to do so for abstract weird Animal:s. An alternative for Animal being a common superclass is letting it be a protocol.
protocol Animal {
static var species: String { get }
var name: String? { get }
var age: Int { get set }
var details: String { get }
}
extension Animal {
static var species: String { return "\(self)".lowercased() }
// since all the properties used in 'details' are blueprinted,
// we might as well supply a default implementation of it.
var details: String {
return "This animal is a \(Self.species)\(name.map{ " named \($0)" } ?? ""), aged \(age)."
}
}
struct Dog: Animal {
let name: String?
var age: Int
init(age: Int, name: String? = nil) {
self.name = name
self.age = age
}
}
var myDog = Dog(age: 3, name: "Fred")
print(myDog.details) // This animal is a dog named Fred, aged 3.
myDog.age = 4 // grats to Fred!
print(myDog.details) // This animal is a dog named Fred, aged 4.
let wildDog = Dog(age: 6) // never named ...
print(wildDog.details) // This animal is a dog, aged 3.
Note that I've chosen to use a class property species to name the species of each animal, and reserved the instance property name for those animals that are given a name; say, your dear dog named Fred (rather than your dear dog named dog).
Using protocols will also make it natural choice to prefer generics over typed abstract types (the latter which might be tempting when using a common superclass):
struct Cat: Animal {
let name: String?
var age: Int
init(age: Int, name: String? = nil) {
self.name = name
self.age = age
}
}
var wildCat = Cat(age: 2)
func yieldBirthday<T: Animal>(for animal: inout T) {
print(animal.details)
animal.age += 1
print("This \(T.species) now had a birthday!")
print(animal.details)
}
yieldBirthday(for: &myDog)
/* This animal is a dog named Fred, aged 4.
This dog now had a birthday!
This animal is a dog named Fred, aged 5. */
yieldBirthday(for: &wildCat)
/* This animal is a cat, aged 2.
This cat now had a birthday!
This animal is a cat, aged 3. */

One way to solve will be to use "template method pattern"
class Animals {
lazy var name : String = self.defaultName
var defaultName:String { return "default" }
func Details()-> String{
return "This animal is a \(name) and has \(age) years old."
}
}
class Dogs : Animals {
override var defaultName:String { return "dog" }
}
Another approach will be to create init method for each subclass and override the default values

Here is how I would do this, by making an initializer to set the name (with a default value):
class Animal: CustomStringConvertible {
let name: String
let age: Int
init(name: String = "default", age: Int = 0) { // FIXME: Does a default age of 0 make sense?
self.name = name
self.age = age
}
public var description: String {
return "This animal is a \(name) and is \(age) years old."
}
}
class Dog: Animal {
init() {
super.init(name: "Dog")
}
}
class Cat: Animal {
init() {
super.init(name: "Cat")
}
}
var myAnimal = Dog()
print (myAnimal)

Related

Some Problems about Opaque Return Type and Protocol with associatedtype In Swift

How to deal with this problem?
Definitions:
protocol NameProtocol {
var rawValue: String { get }
}
struct CatName: NameProtocol {
enum CatNamesEnum: String {
case Tiger, Max, Sam
}
var literal: CatNamesEnum
var rawValue: String {
literal.rawValue
}
}
struct DogName: NameProtocol {
enum DogNamesEnum: String {
case Jack, Toby, Sadie
}
var literal: DogNamesEnum
var rawValue: String {
literal.rawValue
}
}
Definitions:
protocol AnimalProtocol {
associatedtype Name: NameProtocol
var name: Name { get }
func cry() -> String
}
class Cat: AnimalProtocol {
var name: CatName
func cry() -> String {
return "meow, I am \(name.rawValue)"
}
init(name: CatName) {
self.name = name
}
// some other cat actions...
}
class Dog: AnimalProtocol {
var name: DogName
func cry() -> String {
return "bark, I am \(name.rawValue)"
}
init(name: DogName) {
self.name = name
}
// some other dog actions...
}
The code above are some definition code structure, should not be modified.
And the functions below takes some problem:
Protocol with asccociatedtype cannot be the dictionary value type.
Function with Opaque Return Type cannot return some different types extends the same protocol.
// <1>
// Error: Protocol 'AnimalProtocol' can only be used as a generic constraint because it has Self or associated type requirements
let animals: [String: AnimalProtocol] = [
"cat": Cat(name: CatName(literal: .Sam)),
"dog": Dog(name: DogName(literal: .Jack))
// ...
// maybe a lot of animals
]
for (animal, entity) in animals {
print("\(animal): \(entity.cry())")
}
// <2>
// Error: Function declares an opaque return type, but the return statements in its body do not have matching underlying types
func animalCry(animal: String) -> some AnimalProtocol {
switch animal {
case "cat":
return Cat(name: CatName(literal: .Sam))
default:
return Dog(name: DogName(literal: .Toby))
}
}
Any solutions?
Or best practice of different types(with embed generic type) in a list.
You say that the definitions should not be modified but this is exactly what I have done here:
I removed the associated type from the AnimalProtocol protocol since it wasn't used elsewhere in any conforming types
protocol AnimalProtocol {
var name: NameProtocol { get }
func cry() -> String
}
Then I changed both Cat and Dog to conform to the protocol by changing the name declaration in both to
var name: NameProtocol
this resolves the issue with the dictionary and the function was fixed by changing the return type from some AnimalProtocol to AnimalProtocol

I need help to Swift with function

There is this job in Swift 5.0:
The class is presented below. In the body of this class, create a function that will print the parameters of this class for a specific object. Create such an object of class Student, call it this function and display the result on the screen:
Job class
class Student {
var name: String
var surname: String
var yearOfBorn: Int
var mark: Double
init(name: String, surname: String, yearOfBorn: Int, mark: Double) {
self.name = name
self.surname = surname
self.yearOfBorn = yearOfBorn
self.mark = mark
}
}
How i can make it?
I trying:
func printStudent() {
if name == name {
print(name)
} else if surname == surname {
print(surname)
} else if yearOfBorn == yearOfBorn {
print(yearOfBorn)
} else if mark == mark {
print(mark)
}
}
I’m not sure what your intent was with these if statements. Perhaps you are thinking of:
if let foo = foo { ... }
But that technique is only used if foo was an optional. But your properties are not optionals, so if let syntax is unnecessary.
Needless to say, you could just do:
func printStudent() {
print(name)
print(surname)
print(yearOfBorn)
print(mark)
}
FWIW, if your intent is just to print this out for your own purposes, you might want to make your class conform to CustomStringConvertible:
extension Student: CustomStringConvertible {
var description: String { return "<Student name=\(name); surname=\(surname); yearOfBorn=\(yearOfBorn); mark=\(mark)>" }
}
Then you don’t need to write your own printStudent method at all, but can use print directly:
let student = Student(name: "Rob", surname: "Ryan", yearOfBorn: 2000, mark: 4)
print(student)
And that will produce:
<Student name=Rob; surname=Ryan; yearOfBorn=2000; mark=4.0>
Alternatively, if you’re OK with struct value type instead, you don’t need the init method or the CustomStringConvertible protocol, at all. Then you can define Student as simply:
struct Student {
var name: String
var surname: String
var yearOfBorn: Int
var mark: Double
}
And then
let student = Student(name: "Rob", surname: "Ryan", yearOfBorn: 2000, mark: 4)
print(student)
Will produce:
Student(name: "Rob", surname: "Ryan", yearOfBorn: 2000, mark: 4.0)
If you want to print all the attributes of the object you don’t need this if statements; as a matter of fact if you pass name == name as the parameter the first if statement will be always entered and thus the other ones skipped.
You just need to create a function like this where you print each attribute:
func printStudent() {
print(self.name)
print(self.surname)
print(self.yearOfBorn)
...
}
You just need to print the variables:
func printStudent() {
print("Name: \(self.name), Surname: \(self.surname), Year Of Born: \(self.yearOfBorn)")
}
try this code:
func printStudent () {
print("name: \(self.name), surname: \(self.surname), yearOfBorn: \ .
(self.yearOfBorn), mark: \(self.mark)")
}

Swift Initializers and Class Inheritance

I just wanted to ask why does this code print out the quantity 1 even though the RecipeIngredient doesn't provide a default value:
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ✔" : " ✘"
return output
}
}
let item = ShoppingListItem()
print(item.quantity)
The default value is provided by the convenience initializer of RecipeIngredient.
ShoppingListItem() is shorthand for
ShoppingListItem.init() (inherited from `Food) which calls
ShoppingListItem.init(name:) (inherited RecipeIngredient) which calls
ShoppingListItem.init(name: name, quantity: 1) (inherited RecipeIngredient) which calls
ShoppingListItem.init(name: String, quantity: Int) (inherited RecipeIngredient) which ultimately sets quantity to 1
This is some pretty basic debugging. You should really look into how to use the Xcode debugger.

Swift inheritance issue. How to get appropriate class object

I have two classes (to simplify I drop other filed the object is more complicated then the Person):
class Person
{
var name: String = "Default name"
init(object: PersonEntity)
{
name = object.daysMask
}
}
class Employer: Person
{
}
I have function that configure for me a person
func getConiguratedPerson(name: String) -> Person
{
let person = Person()
person.name = name
}
In case if I want to get Person I simple do this:
let person = getConiguratedPerson("Alex")
but what if I need Employer instead and I want to use this function as well
let employer = getConiguratedPerson("Alex") // returns Person as expected but need to have employer instead.
In Objective-C we can simple do this:
Employer *employer = Employer([self getConiguratedPerson:"Alex"]) if I remember.
The mistake is the getConfiguredPerson function. You want an initializer. Initializers return your own class, which is exactly what you want.
class Person {
var name: String = "Default name"
init(name: String) {
self.name = name
}
init(object: PersonEntity) {
self.init(object.daysMask)
}
}
class Employer: Person {}
Now to create a person, you just use Person(name: "Alex") and to get an employer you use Employer(name: "Alex").
Note that this is true in ObjC as well. You shouldn't have getConfiguredPerson there either. You'd should have [Person initWithName:].
You can't use upcasting here, because configured person is not of Employee type, but of Person type. To achieve the desired result I would suggest something like this:
class Person {
var name = "name"
required init() { }
}
class Employee: Person { }
func getPerson<T: Person>(name: String) -> T {
let person = T()
person.name = name
return person
}
let employee: Employee = getPerson("Alex")
or this if type of object returning by factory method depends on input:
func getPerson(name: String) -> Person {
if name != "Alex" {
return Person()
}
return Employee()
}
let employee = getPerson("Alex") as! Employee

Polymorphism in Swift (if not)

I have this code in swift: To explain the idea of polymorphism!
//Polymorphism
class Person {
var name:String="Guest"
var age:Int=0
init(name:String) {
self.name=name
self.age=0
}
init(name:String, age:Int) {
self.name=name
self.age=age
}
func Update(name:String) {
self.name=name
}
func Upgrade() {
self.age++
}
}
class Student:Person
{
var average:Float=100
func IsOk()->Bool {
return average > 80
}
init(name:String, average:Float) {
super.init(name: name)
self.average=average
}
}
class Teacher:Person {
var Salary:Float=2000
init(name:String, age:Int, Salary:Float){
super.init(name: name, age: age)
self.Salary=Salary
}
func GetNetSalary()->Float {
return 0.8*self.Salary
}
override func Upgrade() {
super.Upgrade()
Salary*=1.1 // add 10% to salary
}
}
var t1:Teacher=Teacher(name: "Ahmed", age: 28, Salary: 3000)
var st1=Student(name:"Test", average: 70)
var p1:Person=Person(name: "abc")
var p2:Person=Student(name: "Student1", average: 100) //up casting
var p3:Person=Teacher(name: "Teacher", age: 40, Salary: 3008)
var arr=[t1, st1, p1, p2, p3] //array of persons and teachers and students
for instance in arr {
if instance is Student {println("This is student")}
if instance is Teacher {println("This is teacehr")}
}
In the end in the for loop How could I put such a condition to see if an element in the array is only a Person?
Because when I type:
if instance is Person {println("This is a Person")}
This gives me an error because this condition is always true!
EDITED
You are asking:
How could I put such a condition to see if an element in the array is only a Person?
In a strongly, statically, typed language in which you cannot access the run-time type of an object, you cannot do this. However, in Swift you can use the reflexive properties of the language, through the dynamicType method, to get such a type.
I should say that #Renzo's answer is outdated and dynamicType is deprecated. you must use type(of:) method for that. one more advice: if you want to know the exact type of an optional object, unwrap it first, then pass it to type(of:)
class A { }
class B: A { }
let b: A? = B()
print("\(type(of: b))") // you will get optional(A)
if let temp = b {
print("\(type(of: temp))") // you will get(B)
}
Try this:
for instance in arr {
if instance.dynamicType == Person.self {print("This is a Person")}
if instance.dynamicType == Student.self {print("This is student")}
if instance.dynamicType == Teacher.self {print("This is teacehr")}
}
More information about this you can find in Metatype Type chapter of documentation.
Maybe you can test if the element is aTeacher ? If not, it must be aPerson!