PK - Chapter 3 Flashcards
Declare a class named Foo.
class Foo {
}
What is the default access level of a class ?
Public
Explain class constructors.
- as in Java, an empty constructor is generated by default
- a primary constructor can be specified in the class declaration
- parameters in the primary constructor are _properties
of the instance_ - secondary constructors can be created in the body of
the class using the constructor keyword - constructors can reference other constructors
- secondary constructors must directly/indirectly invoke the
primary constructor; primary must always be invoked
Create a primary constructor for class Foo, with data validation.
class Foo (firstArg: String?, secondArg: Int?) { init { require(! firstArg.isNullOrEmpty()) { "First argument cannot be null or empty." } require(! secondArgument.isNull()) { "Second argument cannot be null or empty." } } }
Note:
require will throw an IllegalArgumentException with the
message provided, if the condition is false.
Explain initializer blocks.
* syntax is init { doSomething() } * initializer blocks are executed in the same order as they appear in the class body * initializer blocks can be interleaved with the property initializers * parameters of the primary constructor can be used in initializer blocks
Explain primary constructors.
- a primary constructor is specified in the class declaration
- any code run as part of the primary constructor needs to be in an init block
- parameters in the primary constructor are properties of the instance,
if they include the “val” or “var” keyword - primary constructor does not require the constructor keyword, if it
does not have any annotations or visibility modifiers
class Foo private constructor(firstArg: Int, secondArg: String) { init { ifNeeded(firstArg } }
Explain secondary constructors.
- secondary constructors can be created in the body of a class using the constructor keyword
- constructors can reference other constructors
- secondary constructors must directly or indirectly call the primary constructor
Given the class
class Person constructor(val firstName: String, val lastName: String) {}
val fooPerson = Person(“John”, “Doe”)
Print the person’s first and last name for instance fooPerson.
println(“${fooPerson.getFirstName} ${fooPerson.getLastName}”)
Note that the getters (and setters for a var) will not be generated for
primary constructor parameters if val and var are not specified. This
will make them private to the class.
What is the recommended visibility
for the constructors or an abstract class ?
protected
They would only be visible to derived classes.
# Define a *_primary constructor_* for class Foo that can *_only be instantiated inside the package_*.
class Foo internal constructor(firstArg: String, secondArg: Int) {
}
Note that there are no “val” or “var” keywords, so the
constructor parameters are not properties of the object.
Summarize visibility levels.
4 visibility levels:
- public
Visible to everyone everywhere - private
Only visible in the file defining it - protected
Only visible to subclasses, extensions
and implementers - internal
Visible from anywhere within the module
What is the access to a private nested class ?
Can create/access an instance from the outer class.
What is the access to an internal nested class ?
Can be created/accessed from anywhere in the module.
What is the access to an protected nested class ?
Can be created/accessed from any class that is derived from the containing class.
What is the difference between static and non-static nested classes ?
Static nested class:
- can only access the public members
of the enclosing class - not defined with the inner keyword
Inner class (non-static class):
- has access to the enclosing class members,
even if declared private - can only be created from an instance of an
enclosing class - defined with the inner keyword
How would you access the “memberOne” variable
in outer class “Alpha”
from inner class “Bravo” ?
this@Alpha.memberOne
Since we are referencing it from an inner class,
the visibility of memberOne does not matter.
How would you access the “memberOne” variable
in outer class “Alpha”
from nested static class “Bravo” ?
this@Alpha.memberOne
The nested static class can only access the member variable if it is public.
Add a MouseListener to a button,
using an anonymous inner class.
button.addMouseListener(adapter : MouseAdapter() {
override fun mouseClicked(event: MouseEvent) { this@OuterClass.doSomething() }
})
# Define a data class named "Person" with attributes "firstName", "lastName".
data class Person(val firstName: String, val lastName: String)
# Define an enum named "DayOfWeek" with attribute "properCaseName".
enum class DayOfWeek(val properCaseName: String) {
MONDAY(“Monday”),
TUESDAY(“Tuesday”),
WEDNESDAY(“Wednesday”),
THURSDAY(“Thursday”),
FRIDAY(“Friday”),
SATURDAY(“Saturday”),
SUNDAY(“Sunday”)
}
What are the built-in properties of an enum instance ?
name: String
ordinal: Int
Create an interface named “Printable” and
an enum named “Word” that implements Printable.
interface Printable { fun print(): Unit }
enum class Word : Printable { HELLO { override fun print() { println("Word is Hello") } }, BYE { override fun print() { println("Word is Bye") } } }
val word = Word.HELLO
word.print()
Create a class with static methods.
Kotlin doesn’t directly support static methods for a class.
Methods accessed statically must be defined
in a companion object.
What is a companion object ?
“class” is used to define classes
that have multiple instances.
"companion object" is used to define a singleton object instantiated at runtime. It is a "class object" defined within a class body.
Create an *_anonymous companion object_* inside class Foo.
class Foo { companion object { fun returnOne() : Int = 1 } }
This can be referenced as:
Foo.a()
Create a *_named companion object_* inside class Foo.
class Foo { companion object Barr { fun returnOne() : Int = 1 } }
This can be referenced as:
Foo.a()
What are the characteristics of a companion object ?
- is a singleton created at runtime for the class
- has access to the private methods
and properties of the containing class’s objects - is only useful if it needs access to the private
members of the containing class; else just
declare “top level” properties and functions in the file - can implement interfaces and extend other classes
- can be anonymous or named
- can only have one anonymouse companion object
Define a static method Foo.bar() that can be called from Java.
class Foo { companion object { @JvmStatic fun bar() {} } }
Create a singleton.
// factory pattern class ServiceManager private constructor(val pName: String) { companion object { fun create(pNewName: String): ServiceManager { return ServiceManager(pNewName) } } }
val myServiceManager = ServiceManager.create(“SomeName”)
How would you access method bar() in
Foo’s companion object explicitly ?
Foo.Companion.bar()
What are the characteristics of an Interface ?
An interface can contain:
- abstract methods
- method implementations
- properties, but not state (no assignments)
Implementing or overriding any of these in a class definition
What is the difference between an interface and an abstract class ?
- an interface cannot contain state, a class can
- a class can implement multiple interfaces,
but only inherit from one abstract class
What are the characteristics of Any ?
- Any is the base class for Kotlin
- if a class does not extend a super class,
it extends Any - Any implements a subset of methods
found in the Java Object class - common methods:
equals(arg list): Boolean
hashCode(): Int
toString(): String
When can you extend a class ?
A class cannot be extended unless it is declared with the *_open_* keyword.
Given the following base class, define a subclass named "CardPayment" with the additional constructor parameters of "description" and "accountNumber".
open class Payment(val payment: BigDecimal) { }
class CardPayment( val pDescription: String, cardNumber: String, val amount: BigDecimal): Payment(amount) { }
What is a sealed class ?
A sealed class cannot be extended.
Classes are sealed by default.
The *_open_* keyword must be used when defining a class that is *_not sealed_*.
Define a sealed class “Foo”.
class Foo { }
Define a class “Foo” that can be extended.
open class Foo { }
# Define class "Foo". Extend class "Bar". Implement (empty/marker) interfaces "Learn" and "More".
class Foo : Bar, Learn, More { }
or
class Foo : Learn, More, Bar { }
or
class Foo : Learn, Bar, More { }
Any ordering of superclass and interface(s) is fine.
Can the scope of a protected member
be expanded to public ?
Yes, if it is defined “open”.
Redefining the field will NOT replace the existing one,
when it comes to object allocation.
WHY DO THIS ???
open class Container { protected open val fieldA: String = "Some value" }
class DerivedContainer : Container() { public override val fieldA: String = "Something else" }
Describe an abstract class.
- specified with the abstract keyword in the class definition
- abstract methods must also include the abstract keyword
- cannot create an instance of an abstract class
- concrete extensions of the class must implement all abstract methods
- abstract class can extend a concrete class, and override parent method
as abstract, if the parent method is open. Parent method will no longer
be accessible
When should you create
an interface versus an abstract class ?
- is-a versus can-do
- versioning - if you add a method to an interface,
all classes that implement that interface must be
updated to implement the new interface - interfaces cannot initialize state
- interfaces do not have a constructor or factory
- subclasses can only extend one superclass/abstract
Explain late-binding polymorphism.
Also known as dynamic binding or runtime binding.
Base classes define and implement virtual methods.
Subclasses can override virtual methods that are open.
When a virtual method is called, the type of the
current receiver is determined at runtime and
the implementation of that type is executed.
I there a runtime performance penalty for
creating deep inheritance hierarchies ?
Yes.
Virtual method calls are expensive,
because they have to search through
the entire class hierarchy at runtime to
determine which implementation to execute.
What are the overriding rules ?
- both methods and properties (val) can be overridden
- by default, methods and properties are closed and cannot be overridden
- a method or property definition must include open to make it available
to be overridden in a subclass - the override keyword must be used when overriding a
method or property in a subclass - you can override a val with a var, but not the other way around
- if a superclass and an interface implement the same method signature,
the subclass must implement the method. The method may call the others:
super<superclass>.method()<br></br> super<interface>.method()</interface></superclass>
What are the 2 types of composition ?
- aggregation - “assocated-with”
The object lifecycle is independent from the
container lifecycle. Container can be destroyed,
but the aggregated objects can live on. - composition - “part-of”
The object lifecycle is tied to the container
lifecycle. If the container is destroyed, so are
the composed objects.
These objects are constructed by the container
and are frequently private.
Why should you
favor composition over inheritance ?
- deep class hierarchies are less efficient at runtime
- single responsibility
- simplicity of design
- reduce type coupling
- greater flexibility
- easy to delegate to a different type
What is a sealed class ?
- defined with the sealed keyword
- abstract class
- can be extended by subclassed
nested in the sealed class - a powerful enumeration option that
supports multiple instances of each
subclass - powerful when used in a collection
with a when statement
Given the following inheritance,
how would you access a method implementation
foo()” in class “A” from class “C”, if it
has been overridden in class “B” ?
A <– B <– C
super.foo()