My code:
open class Club(name: String, country: String)
class FemaleClub(): Club()
var femaleClub = FemaleClub("Blue", "Australia")
Why is the above code not possible?
Why does it have the error
no value passed for parameter name
and
no value passed for parameter country
in my subclass? The values are set when I initiate femaleClub.
In your example parent class Club has primary constructor which is, by language specification, must be called either from secondary constructors of the same class or primary constructor of subclasses to initialize parameters defined in primary constructor. If you don't want to call a primary constructor of a parent class from subclasses you have a couple of options:
Set default values to parameters of primary constructor:
open class Club(name: String = "DefaultName", country: String = "Country") {
}
In this case you will not be able to call primary constructor with params:
// this is not possible
var femaleClub = FemaleClub("Blue", "Australia")
Create secondary constructor in parent class which calls primary constructor:
open class Club(name: String, country: String) {
constructor(): this("Name", "Country")
}
But also you won't be able to call FemaleClub("Blue", "Australia").
To be able to call constructor with parameters of a subclass FemaleClub("Blue", "Australia") you need explicitly define them in primary constructor of the subclass and call parent's primary constructor, i.e.:
class FemaleClub(name: String, country: String): Club(name, country) {}
There are some problems with your code:
FemaleClub does not have a constructor which accepts two arguments, even if the base class has one.
The primary constructor of the inherited class should call the primary constructor of the base class. Because your base class accepts two non-nullable arguments, you have to provide them to it, otherwise your code wont compile.
These issues can be fixed in the following way:
class FemaleClub(name: String, country: String): Club(name, country) {
}
About Kotlin classes and inheritance, read here Classes and Inheritance and here Kotlin Inheritance.
You can use at least four approaches to get what you want:
First approach (default values):
open class Club(var name: String = "Blue", var country: String = "Australia") { }
public class FemaleClub(): Club() { }
fun main() {
var femaleClub = FemaleClub()
println("RESULT: ${femaleClub.country}")
}
// RESULT: Australia
Second approach (passing values):
open class Club(var name: String, var country: String) { }
class FemaleClub(): Club("Green", "NZ") { }
fun main() {
var femaleClub = FemaleClub()
println("RESULT: ${femaleClub.country}")
}
// RESULT: NZ
Third approach (init block):
open class Club(name: String, country: String) {
var name: String = "Blue"
var country: String = "Australia"
init {
this.name = name
this.country = country
}
}
class FemaleClub(): Club("Green", "NZ") { }
fun main() {
var femaleClub = FemaleClub()
println("RESULT: ${femaleClub.country}")
}
// RESULT: NZ
Fourth approach (class sec.constructor):
open class Club {
var name: String
var country: String
constructor(name: String = "Blue", country: String = "Australia") {
this.name = name
this.country = country
}
}
class FemaleClub(): Club() { }
fun main() {
var femaleClub = FemaleClub()
println("RESULT: ${femaleClub.country}")
}
// RESULT: Australia
In FemaleClub you are not specifying what should be passed to Club as its 2 String arguments, name and country.
And you are not specifying anywhere that FemaleClub takes 2 strings as constructor arguments, so the last line wont work either.
You need to invoke the constructor (primary/secondary) from the class you want to inherit from.
Club only has one constructor, the primary constructor. It takes two strings as parameters. Club does not have an empty constructor.
So, it cannot be invoked as Club().
I would propose two changes.
1) Make name and country of Club properties
open class Club(name: String, country: String)
2) Declare parameters in FemaleClub's primary constructor
Since you want to be able to specify values for name and country when you instantiate FemaleClub it would be a good idea to give the primary constructor of FemaleClub name and country as parameters.
Additionally I would recommend using named parameters when passing on the values, since you can easily mix up the two strings (resulting in passing name as country and country as name.
class FemaleClub(name: String, country: String): Club(name = name, country = country)
Related
I have a Scala class as shown:
class MyClass(title: String, value: Int) {
...
}
If the constructor is called with a title value of null, I would like to set title to the empty string instead. How can I do that? Is there a cleaner way than forcing title to be private and providing a getter?
def getTitle: String = if (title == null) "" else title
You can create a factory method that provides the desired value. Often, in Scala, this is done in the companion object:
object MyClass {
def apply( title: String, value: Int ): MyClass =
new MyClass( if (title == null) "" else title, value)
}
As it stands your title value is just a constructor parameter so it is not accessible from outside (did you omit a val?). You can use this fact to compute the real title member like this:
class MyClass(_title: String, val value: Int) {
val title = if (_title == null) "" else _title
...
}
This guarantees that title is not null in any instance of MyClass
For completeness, here is the alternative factory method implementation:
trait MyClass {
def title: String
def value: Int
}
object MyClass {
protected class MyClassImplementation(val title: String, val value: Int) extends MyClass {}
def apply(title: String, value: Int) =
new MyClassImplementation(if (title == null) "" else title, value)
}
The only way to create an instance of MyClass is via the factory method, so the null check is always called.
Instead of using nullable variables that requires constant checking if-not-null, Scala encourages you to use Option where a value can be "none".
One way of achieving this is to use a secondary-constructor:
class ClassX(title: Option[String]) {
def this(title: String) {
this(Option(title))
}
}
And if you have to use a nullable variable, you could use the factory mentioned above.
I want to load files depending on the name of the class that extends my base class. Given the following classes:
class LoaderBase {
var data: String = stuff(extender.getClass().getName()+".json")
}
class FooBar extends LoaderBase {
var something: String = data
}
Is it possible without manually passing the used class' name to the base class?
use this can get the current Instance class, so you can do it like:
var data: String = this.getClass.getSimpleName
I have enum with cities that looks like
enum Cities: String {
case la = "Los Angeles"
case ta = "Tel Aviv"
case ny = "New York"
}
And I need to pass sting with city name to one of functions that is waiting for NSString
so this code is not working and I don't understand why
let selectedCity = Cities.ny
myFunc(city : selectedCity)
I get error "Cannot convert value of type 'Cities' to expected argument type 'String!'"
Xcode is suggesting me to use Cities.ny.rawValue as selectedCity.
Swift is strongly typed
So when you pass a value to a function argument the compiler checks that it is of the correct type.
Example
Let's declare a function that receive an Int as parameter
func giveMeYourAge(n: Int) {
}
And now let's try to invoke that function passing something that is NOT an Int
giveMeYourAge(n: "Hello World")
Now the compiler gets angry with us ☠️
error: cannot convert value of type 'String' to expected argument type 'Int'
giveMeYourAge(n: "Hello World")
And it is right (of course). Infact it is telling us that we cannot pass a String to a function that expects an Intindeed.
Your code
When you declare you enum (let's call it City as properly suggested by #Honey) you create a new type City.
enum City: String {
case la = "Los Angeles"
case ta = "Tel Aviv"
case ny = "New York"
}
City is something TOTALLY DIFFERENT from a String.
And we cannot use City in place of a String or viceversa. (Just like in the previous example Int was totally different from String).
So if you have a function that expects a String as parameter
func myFunc(city : String) {
// ...
}
of course you won't be able to pass an value of type City to it.
Wait! What about Polymorphism?
Now let's look at this code
class Animal { }
class Dog: Animal { }
func feed(animal: Animal) {
}
let dog = Dog()
feed(animal: dog)
Why does this code compile??? 😨
This is called polymorphism. When we write
class Dog: Animal { }
we define a new type (Dog) that is more specific than Animal.
Polymorphism does allow us to pass to a function that expects an Animal as param, something that is more specific (like a Dog).
However polymorphism has nothing to do with your example. Even if the syntax is similar.
When you define an enum using this syntax
enum City: String { ... }
You are NOT defining a subtype of String.
You are NOT creating something more specific than a String.
You are NOT using polymorphism.
Instead you are simply creating a new type City that has an instance property (rawValue) of type String.
You need to specify raw value to use the string literal, because without that you are just referring to the enum itself.
let selectedCity = Cities.ny.rawValue
I have better solution, which is much more reasonable... therefore you do this:
enum Cities {
case la
case ta
case ny
var cityString: String{
switch self{
case la:
return "Los Angeles"
case ta:
return "Tel Aviv"
case ny:
return "New York"
}
}
}
Now you need to call just this:
let selectedCity = Cities.ny.cityString
the reason behind me voting over other ideas is that rawValues should be used for recognizing the case, not for getting the values of them... But that's just my opinion :)
It's like you're trying to access a class instance, rather that the property of the instance. It won't work. Specifically what you're trying here is accessing the enum itself which is incorrect. You should access its rawValue property.
From Apple:
You access the raw value of an enumeration case with its rawValue
property:
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3
i have a class UserContact which have an enum in the constructor
class UserContact( val uuid: Int ,
var phone: String ,
var sms: Boolean ,
var pushNotification: Boolean ,
var deviceType: UserDeviceType , // Enum
var jabberID: String ){
}
now i want to write an auxiliary constructor in this class but i dont know how to give default value for the enum
i have tried with "_" but it did not work
def this(){
this(0, "", false , false , _ , "")
}
please help
You need to put in some actual value. Just look at UserDeviceType values and choose which one you want in this constructor. But you should think if you really want an auxiliary constructor like this; it looks like a bad design in most circumstances, as Peter Neyens' comments explain.
The "default" value produced by var x: SomeEnumType = _ is null, same as for any object type (including String), but this is rarely what you want (it should only be used if the variable is guaranteed to be initialized later).
class Person {
var name: String
var age: Int
func init(age: Int, name: String, /** ... **/) {
self.age = age
self.name = name
// ... much typing, much boring.
}
}
I may be a bit lazy but explicitly typing each property out feels a lot like the human compiler at work. Is there any syntax sugar for assigning constructor argument to an instance in Swift?
Check out the Default Initializers section of the Swift language book. If you were to make Person a struct instead of a class, it would automatically get a memberwise initializer:
struct Person {
var name: String
var age: Int
}
let p = Person(name: "Joe", age: 30)
Classes or structs that define default values for all their stored properties get a default initializer:
class Person {
var name: String = ""
var age: Int = 0
}
var p = Person()
p.name = "Joe"
p.age = 30
These automatically generated initializers disappear if you declare any of your own initializers in the original type declaration, but you can add other initializers in an extension.