Untitled
unknown
plain_text
a year ago
36 kB
6
Indexable
kotlin.oop.features 1. delegation 1.1. BuildingDelegate.kt import kotlin.reflect.KProperty import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadOnlyProperty /* Implementing the delegate - thisRef: it must be the same type as, or a supertype of, the class owning the delegating property. It represents the object that contains the delegated property and can be used to access other members of the object. - property: it must be of type KProperty<*> and can be used to access the metadata of the delegated property. */ object Formatter { private var value: String = "" operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return value } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { this.value = value.replace(Regex("[aeiouAEIOU]"), "").uppercase() } } class Example { var firstProp: String = "" set(value) { // Remove all vowels and convert the remaining string to uppercase field = value.replace(Regex("[aeiouAEIOU]"), "").uppercase() } var secondProp: String = "" set(value) { // Remove all vowels and convert the remaining string to uppercase field = value.replace(Regex("[aeiouAEIOU]"), "").uppercase() } var thirdProp: String by Formatter var fourthProp: String by Formatter } class Delegate { private var curValue = "" operator fun getValue(thisRef: AnotherExample, property: KProperty<*>): String { println(property.name + ": " + thisRef.stringProp + thisRef.foo()) // thisRef allows us to access any member of the class: AnotherExample return curValue } } class AnotherExample { val stringProp: String by Delegate() fun foo(): String { return "" } } fun implementingTheDelegate() { val example = Example() example.thirdProp = "Thieu" println(example.thirdProp) // TH } /* Anonymous delegates Simply use the ReadOnlyProperty and ReadWriteProperty interfaces from the Kotlin standard library. - getValue() is declared in ReadOnlyProperty. - ReadWriteProperty extends it and adds setValue(). */ fun anonymousDelegate() = object : ReadWriteProperty<Any?, String> { // we define a function called anonymousDelegate that returns an instance of an anonymous object implementing // the ReadWriteProperty interface for a property of type String. // You might have noticed that we are delegating properties directly from inside the main function and they are not // declared inside a class. This is another feature provided by Kotlin, and it is called local delegated properties, // which means you can use delegated properties not only in classes, but also as local variables within functions. var curValue = "" override fun getValue(thisRef: Any?, property: KProperty<*>): String { return curValue } override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { curValue = value println("The new value of ${property.name} is: $value") } } /* Delegating to another property In Kotlin, you can delegate a property to another property, allowing you to reuse the behavior of an existing property for a new property. */ class Example2 { private var _counter = 0 var counter: Int get() = _counter set(value) { _counter = value println("Counter set to $value") } var anotherCounter: Int by this::counter } /* Best practices 1. Use standard delegates when possible. 2. Keep your delegated properties focused: Each delegated property should do one thing well. 3. Avoid unnecessary delegation. */ fun main(args: Array<String>) { implementingTheDelegate() val readOnlyString: String by anonymousDelegate() var readWriteProperty: String by anonymousDelegate() readWriteProperty = "Hello" val example = Example2() val count = example::counter example.counter = 10 println(count.get()) // 10 } ############################# 1.2. ClassDelegation.kt package oop.features.delegation /* Class delegation is a mechanism in Kotlin that allows you to delegate the implementation of an interface or functionality of one class to another class. The keyword by is used in Kotlin to delegate the implementation of an interface or functionality of one class to another class. Differences between inheritance and delegation 1. Inheritance: - A class inherits the functionality and properties of the parent class. - There is a hard link between the classes. - There is a possibility of multiple inheritance problems, such as the "diamond problem". 2. Delegation: - The implementation of an interface or functionality is passed to another class. - It provides flexibility and modularity and helps avoid multiple inheritance problems. */ // Step 1: Creating an interface interface Drawable { fun draw() } // Step 2: Creating a delegate class class Circle : Drawable { override fun draw() { println("Drawing a circle") } } class Rectangle : Drawable { override fun draw() { println("Drawing a rectangle") } } // Step 3: Creating a class that uses delegation class DrawingBoard(private val drawable: Drawable) : Drawable by drawable fun main() { // var circle: Drawable = Circle() // var rectangle: Drawable = Rectangle() // // fun draw(drawable: Drawable) = drawable.draw() // draw(circle) // draw(rectangle) val circle = Circle() val rectangle = Rectangle() val drawingBoard1 = DrawingBoard(circle) drawingBoard1.draw() val drawingBoard2 = DrawingBoard(rectangle) drawingBoard2.draw() } ################################### 1.3. Delegate.kt /* In object-oriented programming (which is the case of Kotlin), for example, the main tool for code reusage is inheritance (and composition, consequently). Delegation is a process of using a certain object instead of providing implementation */ interface MyInterface { fun print() val msg: String } class MyImplementation : MyInterface { override fun print() { println(msg) } override val msg: String = "MyImplementation sends regards!" } // when we need to use the implementation of the interface, we just reference the already existing implementation, // and Kotlin does the rest. class MyNewClass( base: MyInterface // Here we expect an implementation of MyInterface as a parameter (named "base") ) : MyInterface by base // And here we state that MyInterface is implemented by the previously obtained parameter, the one named "base" { override val msg = "Delegate sends regards." // The code looks like this: // We create an instance of class, implementing MyInterface // val delegate = MyImplementation() // Then we pass this implementation instance as a parameter // val delegatingObj = MyNewClass(delegate) // println(delegatingObj.msg) } fun main() { val delegate = MyImplementation() val delegatingObj = MyNewClass(delegate) println(delegatingObj.msg) // Delegate sends regards. delegatingObj.print() // MyImplementation sends regards! // MyNewClass khong co print(), nhung co co doi tuong base la mot trien khai cua MyInterface. // No co print() va duoc goi khi chung ta viet delegate.print(). // Vi vay, MyNewClass chi giao nhiem vu (delegate task) cho MyImplementation (The delegate) va in ra "MyImplementation sends regards!" val loggerInstance = BasicLogger() val dateTimeNotifier = ConsoleNotifier(loggerInstance) val simpleParser = ExampleParser(dateTimeNotifier, loggerInstance) simpleParser.start() // [05.11.2022-14:31:04]: OnBefore! // [05.11.2022-14:31:04]: Parsing... // [05.11.2022-14:31:04]: OnAfter! } // Callback and Logger example // Defines the contract for callbacks interface ICallbackReceiver { fun onBeforeAction() fun onAfterAction() fun action(function: () -> Unit) { onBeforeAction() function() onAfterAction() } } // Defines the contract for logging interface ILogger { fun getStubDateTime() = "05.11.2022-14:31:04" // placeholder date and time val format: String get() = "[${getStubDateTime()}]: " fun print(s: String) } // Simple implementation of ILogger interface class BasicLogger : ILogger { override fun print(s: String) = println(format + s) } // Implementation of ICallbackReceiver that uses BasicLogger for printing class ConsoleNotifier(logger: ILogger) : ICallbackReceiver, ILogger by logger { val onBeforeStr = "OnBefore!" val onAfterStr = "OnAfter!" // "print" is delegated to "logger" override fun onBeforeAction() = print(onBeforeStr) override fun onAfterAction() = print(onAfterStr) } // Class implementing both interfaces by delegation class ExampleParser(notifier: ConsoleNotifier, logger: BasicLogger) : ICallbackReceiver by notifier, ILogger by logger { fun start() = action { parseFiles() } fun parseFiles() { print("Parsing...") // do some file parsing } } ################################# 1.4. StandardDelegates.kt import kotlin.properties.Delegates /* In addition to class delegates, Kotlin has a robust functionality known as delegated properties. This feature allows the delegation of getter and setter methods of a property to another object. */ /* 1. Overview of delegated properties Unlike traditional properties, delegated properties are not backed by a class field. Instead, they delegate getting and setting to another piece of code. This abstraction allows for shared functionality between similar properties. For example: class Example { var p: String by Delegate() } The delegate has the getValue() and setValue() methods, which take over the get() and set() methods */ /* 2. Standard delegates - Lazy properties: the value is computed only during the first access. - Observable properties: listeners are notified about changes to this property. - Vetoable properties: allowing a lambda function to decide if a new value should be accepted or rejected. - NotNull properties: a property delegate for a non-null property that must be initialized before it is accessed. - Storing properties in a map: instead of using a separate field for each property, properties can be stored in a map. */ /* 3. Practical use cases - Lazy initialization: it is useful for properties that are expensive to compute or that might not be needed at all. - Observing property changes: such as updating the UI or validating the new value. - Vetoing property changes: ensuring that a value remains within a certain range or meets certain criteria. - NotNull properties: useful for ensuring that certain preconditions are met before an object is used. - Storing properties in a map: useful for dynamic data structures or for serializing and deserializing objects. */ // Lazy properties: fun lazyProperties() { // The lazy function takes a lambda and returns an instance of Lazy<T>, which serves as a delegate for implementing // a lazy property. // The first call to get() executes the lambda passed to lazy() and remembers the result. Subsequent calls to get() // simply return the remembered (cached) result. val lazyValue: String by lazy { print("Computed! ") "Hello" } println(lazyValue) // Computed! Hello println(lazyValue) // Hello -> Cached Value } // Observable properties class User { var rank: String by Delegates.observable("<no rank>") { prop, old, new -> println("${prop.name}: $old -> $new") } } fun observableProperties() { // The observable delegate allows for a lambda to be triggered any time the value of the property changes, // resulting in change notifications or updating of other related properties. val user = User() user.rank = "first" // rank: <no rank> -> first println(user.rank) // first user.rank = "second" // rank: first -> second println(user.rank) // second } // Vetoable properties fun vetoableProperties() { // The lambda function is called before a new value is set, and it allows the function to decide if the new value // should be accepted or rejected. var max: Int by Delegates.vetoable(0) { prop, old, new -> new > old } println(max) // 0 max = 10 println(max) // 10 max = 5 println(max) // 10 } // NotNull properties class Person{ var name: String by Delegates.notNull() } fun nonnullProperties() { // notNull is a property delegate for a non-null property that must be initialized before it is accessed. val person = Person() person.name // Throws IllegalStateException: // Property name should be initialized before get. person.name = "Ahmed Omar" println(person.name) // Prints "Ahmed Omar" } // Storing properties in a map class User2(val map: MutableMap<String, Any?>) { var name: String by map var age: Int by map } fun storingPropertiesInAMap() { // Properties can be stored in a MutableMap or Map to back mutable or immutable properties, respectively. val user = User2(mutableMapOf( "name" to "Ahmed Omar", "age" to 25 )) println(user.name) // Prints "Ahmed Omar" println(user.age) // Prints 25 user.name = "Ahmed Omar" user.age = 30 println(user.name) // Prints "Ahmed Omar" println(user.age) // Prints 30 } fun main() { observableProperties() } ##################################### 2. ObjectExpressions.kt /* Object expressions in Kotlin allow creating anonymous objects for specific tasks. These objects are instances of a class created on-the-fly and have no name. */ interface ClickListener { fun onClick() } class Button { private var clickListener: ClickListener? = null fun setOnClickListener(listener: ClickListener) { clickListener = listener } fun click() { clickListener?.onClick() } } fun main() { // objectExpressions() example1() example2() } fun objectExpressions() { val anonymousObject = object { val x = 10 val y = 20 fun printSum() { println("Sum: ${x + y}") } } anonymousObject.printSum() // Output: Sum: 30 val clickListener = object : ClickListener { override fun onClick() { println("Button clicked!") } } clickListener.onClick() // Output: Button clicked! // Anonymous objects have a limited scope. If you want to use an anonymous object as a variable value, // you need to specify the variable type explicitly: val listener: ClickListener = object : ClickListener { override fun onClick() { println("Button clicked!") } } listener.onClick() // Output: Button clicked! } fun example1() { val button = Button() button.setOnClickListener(object : ClickListener { override fun onClick() { println("Button clicked!") } }) button.click() } data class Person(val name: String, val age: Int) fun example2() { val people = listOf( Person("Alexander", 25), Person("Elena", 30), Person("Dmitry", 21) ) val sortedByAge = people.sortedWith(object : Comparator<Person> { override fun compare(o1: Person, o2: Person): Int { return o1.age.compareTo(o2.age) } }) println(sortedByAge) } // In Kotlin, you can use an object instead of the classic singleton. // However, sometimes you may need to replace a singleton with a regular class. interface Database { fun connect() } open class MySQLDatabase : Database { override fun connect() { println("Connecting to MySQL...") } } fun example3() { val database: Database = object : MySQLDatabase() { override fun connect() { println("Connecting to a new database...") } } database.connect() // Output: Connecting to a new database... } ###################################### kotlin.oop.generics 1. GenericAndAny.kt /* Generics enable us to parameterize types when defining classes (or interfaces) and methods. Parameterized types make it possible to reuse the same code while processing different concrete types. However, there is also another way to reuse code. If we declare a field of type Any, we can assign a value of any reference type to it. */ class GenericType<T>(val t: T) { fun get(): T { return t } } class NonGenericClass(val value: Any) { fun get(): Any { return value } } fun advantageOfGeneric() { val nonGenericInstance: NonGenericClass = NonGenericClass("abc") // We cannot get a string directly from the method. // val str: String = nonGenericInstance.get() // Compile-time error: Type mismatch var str: String = nonGenericInstance.get() as String // "abc" val instance: NonGenericClass = NonGenericClass(123) val string: String = instance.get() as String // throws java.lang.ClassCastException // Generic is no need perform an explicit type-cast, we never get a runtime exception. // If we do something wrong, we can see it at compile-time. val stringInstance: GenericType<String> = GenericType<String>("abc") str = stringInstance.get() // There is no type-casting here // val num: Int = stringInstance.get() // It does not compile // if the field type in the class is Any, you can easily and with no errors assign values of different types to the field. } fun main() { val stringInstance: GenericType<String> = GenericType<String>("abc") val anyInstance: NonGenericClass = NonGenericClass("abc") println(stringInstance.get()) println(anyInstance.get()) } ###################################### 2. IntroToGenericProgramming.kt import kotlin.reflect.typeOf /* Generics represent parameterized types. Generic methods and classes can handle different types in the same general way. */ // Generic classes class Box<T>(t: T) { /* Constructor accepts * a variable of "some type" * and sets it to a field */ var value = t // A field of "some type" get() = field set(value) { field = value; } } // Generic and several type parameters class Three<T, U, V>(var first: T, var second: U, var third: V) class Pair<T, P>(var first: T, var second: P) { fun changeFirst(newValue: T) { first = newValue } fun changeSecond(newValue: P) { second = newValue } fun printData() { println("Values:") println("first = $first") println("second = $second") } } fun genericAndSeveralTypeParameters() { val nameLastname: Pair<String, String> = Pair("John", "Smith") val nameAge: Pair<String, Int> = Pair("Kite", 18) nameLastname.changeFirst("John") nameLastname.changeSecond("Smith") nameAge.changeFirst("Kate") nameAge.changeSecond(19) nameLastname.printData() nameAge.printData() } // Naming convention for type parameters //T – Type; //S, U, V, etc. – 2nd, 3rd, 4th types; //E – Element (often used by different collections); //K – Key; //V – Value; //N – Number. // Creating objects of generic classes fun creatingObjects() { var obj1: Box<Int> = Box<Int>(123) var obj2: Box<String> = Box<String>("abc") // But if the type is a standard one like Int, String, Double, etc., you can omit the type argument, as the compiler infers it: obj1 = Box(123) obj2 = Box("abc") val obj3 = Three<String, Int, Int>("abc", 1, 2) } // Creating your own collection class RandomCollection<T>(private val items: List<T>) { fun get(index: Int): T { return items[index] } fun length(): Int { return items.size } } fun creatingYourOwnCollection() { var nums = RandomCollection(listOf(1, 2, 3, 4)) for (i in 0 until nums.length()) { print("${nums.get(i)} ") // "1 2 3 4 " } } // Getting type of a variable fun gettingTypeOfAVariable() { val fromExplicitType = typeOf<Int>() println(fromExplicitType) // int (Kotlin reflection is not available) } fun main() { gettingTypeOfAVariable() } ###################################### kotlin.oop.usage 1. dataclass 1.1. DataClass.kt /* The Client class has 3 properties, in order to properly compare the objects (i.e., by their properties) we need to implement equals() and hashCode() functions Why do we need such a long piece of code just for standard stuff? That's the right question because with the data class we can simplify it The data class is a convenient way to organize data or to create DTOs (Data Transfer Objects). */ class AnotherClient(val name: String, val age: Int, val gender: String) { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false other as AnotherClient if (name != other.name) return false if (age != other.age) return false if (gender != other.gender) return false return true } override fun hashCode(): Int { var result = name.hashCode() result = 31 * result + age result = 31 * result + gender.hashCode() return result } } data class Client(val name: String, val age: Int, val gender: String) { // 1. Chi co the tin tuong vao nhung thuoc tinh trong constructor. Moi function se khong xem xet balance vi no nam // ngoai constructor // 2. Co the overriding moi function ngoai tru copy() // 3. Primary constructor phai co it nhat mot tham so va toan bo tham so phai la val hoac var var balance: Int = 0 override fun toString(): String { return "Client(name = '$name', age = $age, gender = '$gender', balance = $balance)" } // Neu khong override ham toString() thi khi thuc hien println(bob) se in ra: // Client(name=Bob, age=29, gender=Male) } fun copyAndComponentN() { val bob = Client("Bob", 29, "Male") val john = bob.copy(name = "John") println(bob) // Client(name='Bob', age=29, gender='Male', balance=0) println(john) // Client(name='John', age=29, gender='Male', balance=0) // Neu khong override ham toString() thi khi thuc hien println(bob) se in ra: // Client(name=Bob, age=29, gender=Male) println(bob.name) // Bob println(bob.component1()) //Bob println(bob.age) // 29 println(bob.component2()) // 29 println(bob.gender) // Male println(bob.component3()) // Male // destructuring val (name, age, gender) = bob println(name) // Bob println(age) // 29 println(gender) // Male } fun main() { copyAndComponentN() } #################################### 1.2. DestructureDeclarations.kt /* 1. Basic destructuring We can separate all variables from the class and work with them as separate objects. This feature is called a destructuring declaration. A destructuring declaration creates multiple variables at once. A destructuring declaration uses a componentN() operator, that returns an n-th element from the class. */ data class User(val name: String, val age: Int, val isAdmin: Boolean) fun basicDestructuring() { val anonym = User("Anonym", 999, false) val (userName, userAge, isAdmin) = anonym println(userName) // prints Anonym println(userAge) // prints 999 println(isAdmin) // prints false } /* 2. Destructuring without data classes Destructuring can be used without data classes as well. We simply need to define a componentN operator manually. componentN functions work by relying on the position of each class variable. */ class User2(val name: String, val age: Int, val isAdmin: Boolean){ operator fun component1(): String = name operator fun component2(): Int = age operator fun component3(): Boolean = isAdmin } fun checkIsAdmin(suspiciousUser: User) { // destructuring val (name, age, isAdmin) = suspiciousUser if (isAdmin) println("Have a nice day!") else println("Sorry, you should not be here.") } /* 3. Destructuring with lists and loops Destructuring declarations also work with lists and loops because List is a class with the implemented componentN operator. */ fun withListsAndLoops() { val list = mutableListOf( User("Han", 24, true), User("John", 22, false), User("Bob", 25, true), User("Cruise", 28, false) ) var (user1, user2, user3) = list // var (user1, user2, user3, user4, user5) = list // error // If the list has more than 3 elements, the remaining ones will not be processed and the program will continue its // work. In the same way, if the list has less than 3 elements, there will be an error and the program will crash. } /* 4. Underscoring for variables When we start using destructuring declarations, the Kotlin compiler may warn us about unused variables in the destructuring declaration. The default IDE solution is to rename unused variables as "_" (underscore) */ fun underscoringForVariable() { val list = mutableListOf( User("Han", 24, true), User("John", 22, false), User("Bob", 25, true), User("Cruise", 28, false) ) var (_, _, user3) = list var user4 = list.component4() println(user3) println(user4) } fun main() { basicDestructuring() withListsAndLoops() underscoringForVariable() } ################################### 2. enumsandsealed 2.1. Enum.kt fun main() { val red = Rainbow.RED println(red.color) red.printFullInfo() println(red.name) // RED println(red.ordinal) // 0 println(isRainbow("black")) // false println(Rainbow.valueOf("RED")) // RED println(3.24.toLong()) } fun isRainbow(color: String) : Boolean { for (enum in Rainbow.values()) { if (color.uppercase() == enum.name) return true } return false } enum class Materials { GLASS, WOOD, FABRIC, METAL, PLASTIC, CERAMICS, CONCRETE, ROCK } enum class Rainbow(val color: String, val rgb: String) { RED("Red", "#FF0000"), ORANGE("Orange", "#FF7F00"), YELLOW("Yellow", "#FFFF00"), GREEN("Green", "#00FF00"), BLUE("Blue", "#0000FF"), INDIGO("Indigo", "#4B0082"), VIOLET("Violet", "#8B00FF"), NULL("", ""); fun printFullInfo() { println("Color - $color, rgb - $rgb") } fun findByRgb(rgb: String): Rainbow { for (rainbow in Rainbow.values()) { if (rgb == rainbow.rgb) { return rainbow } } return NULL } } ###################################### 2.2. Exercise.kt fun main() { println(Currency.USD.ordinal) val color = readln() println(Rainbows.valueOf(color.uppercase()).ordinal + 1) } enum class Currency(val country: String) { USD("United States dollar"), EUR("Euro"), GBP("Pound sterling"), RUB("Russian ruble"), UAH("Ukrainian hryvnia"), KZT("Kazakhstan teenage"), CAD("Canadian dollar"), JPY("Japanese yen"), CNY("Chinese yuan"); } enum class DangerLevel(private val level: Int) { HIGH(3), MEDIUM(2), LOW(1); fun getLevel(): Int { return level } } enum class Rainbows(val color: String) { RED("Red"), ORANGE("Orange"), YELLOW("Yellow"), GREEN("Green"), BLUE("Blue"), INDIGO("Indigo"), VIOLET("Violet"); } enum class DaysOfTheWeek { // write here SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } #################################### 2.3. SealedClass.kt /* 1. Basic syntax A sealed class is abstract, so it can't be instantiated. However, of course, you can extend it. Like with normal classes, you can declare constructors, but constructors in a sealed class must be private or protected: */ sealed interface CustomErrors sealed class CustomError { constructor(type: String) {} // protected (default) private constructor(type: String, code: Int) {} // private // public constructor() {} // Public gives error //primary constructor sealed class CustomError(type: String) } /* 2. Sealed class vs enum Basically, a sealed class is like an enum but with more flexibility. Enum constants have only one single type, while sealed classes offer multiple instances with greater flexibility. We can conclude that an enum is used to represent a fixed set of values, while a sealed class is a class used to represent a fixed set of subclasses of the type of the given class. An enum can't inherit from classes of interfaces, while a sealed class can. */ //enum class Staff(numberOfLessons: Int) { // TEACHER(2), MANAGER("Manager is managing") //} // That is not possible with an enum but can be done with a sealed class: sealed class Staff { class Teacher(val numberOfLessons: Int) : Staff() class Manager(val responsibility: String) : Staff() object Worker : Staff() } open class Person { fun whoAmI(name: String): String { return "I am $name" } } sealed class Staff2 : Person() { class Teacher(val numberOfLessons: Int) : Staff2() class Manager(val responsibility: String) : Staff2() object Worker : Staff2() } fun main() { val worker = Staff2.Worker println(worker.whoAmI("Worker")) val teacher = Staff2.Teacher(24) } ######################## 3. ExtensionFunction.kt /* Extension function cho phep them cac phuong thuc moi vao lop da ton tai ma khong thay doi ma nguon cua lop do */ class Client(val name: String, val age: Int) { fun sayHello() = "Hello" } fun main() { println("Ha".repeated(5)) val client = Client("Thieu", 24) println(client.getInfo()) println(client.sayHello()) // Hello println(client.sayHello("Hien")) val a = 123 println(a.lastDigit()) } fun String.repeated(number: Int = 2): String { return if (number <= 1) { this } else { val newString = StringBuilder() for (i in 1..number) { newString.append(this) } newString.toString() } } fun Int.lastDigit(): Int { return this % 10 } fun Client.getInfo() = "$name - $age" fun Client.sayHello() = "Hi" fun Client.sayHello(name: String) = "Hi $name" ############################# 4. PackagesAndImports.kt import oop.basic.Patient as MyPatient /* 1. Packages In a package, we group classes, functions, and/or variables according to a specific use case or functionality. To define a package, we use a package header. A package contains one or more Kotlin files, with files linked to a package using the same package header. One file or class can use zero or multiple packages. If the package is not specified, the contents of such a file belong to the default package with no name. 2. Default imports The are a number of packages that are imported into every Kotlin file by default: kotlin.* kotlin.annotation.* kotlin.collections.* kotlin.comparisons.* kotlin.io.* kotlin.ranges.* kotlin.sequences.* kotlin.text.* Some packages are imported depending on the target platform: JVM: - java.lang.* - kotlin.jvm.* JS: - kotlin.js.* 3. Import alias Example: package packages import packageone.Person import packagetwo.Person as PersonAdavanced fun main() { val person = Person("John", 30) val adavanced = PersonAdavanced("John", 30) } */ data class Person(val name: String, val age: Int) fun sayHello() { println("Hello") } fun main() { val person = oop.usage.Person("John", 30) oop.usage.sayHello() val myPatient = MyPatient() } ##################################### 5. PairAndTriple.kt /* 1. Pair Pair is a simple Kotlin datatype to store two values with the same or different types in a single instance. There doesn't need to be any relationship between the two values. Two pairs are equal if both their components are equal. */ fun myPair() { val pair1 = Pair(1, "one") println(pair1) // (1, one) // We can use the infix function to to create a new Pair object: val pair2 = 1 to "one" println(pair2) // (1, one) println(pair1 == pair2) // true } /* 2. How to work with Pair To work with the values, we can use first and second properties or the componentN method to extract the values from a Pair. With Pair, we can use two special methods – toString() and toList(): - toString() returns the string representation of the Pair including its first and second values - toList() converts the Pair into a list Besides, we can use the copy() method to copy Pair objects and change their properties using the name of the parameter */ fun workWithPair() { val pairOne = Pair("Hi", "I am a Pair") val pairTwo = "Hi" to "I am another Pair" // Properties println(pairOne.first) // Hi println(pairOne.second) // I am a Pair // Methods println(pairTwo.component1()) // Hi println(pairTwo.component2()) // I am another Pair // toString() val pair = Pair("marks", listOf(8.0, 9.0, 10.0)) println(pair) // (marks, [8.0, 9.0, 10.0]) toString() is implicit println(pair.toString()) // (marks, [8.0, 9.0, 10.0]) // toList() println(pair.toList()) // [marks, [8.0, 9.0, 10.0]] // copy() val other = pair.copy() val grades = pair.copy(second = listOf(9.0, 7.0, 8.5)) } /* 3. Triple Triple, like Pair, is a simple Kotlin datatype that represents a triad of values with the same or different types in a single instance. Like in the case of Pair, the type of each property of a Triple object can also be derived from the context. */ fun myTripe() { val triple = Triple(1, "A", true) println(triple) } fun main () { myPair() } #################################### 6. ToString.kt import java.awt.Point /* The toString() function exists specifically to represent objects as strings. */ fun defaultBehavior() { val nonString = 1.0 println(nonString.toString()) // 1.0 println(nonString) // 1.0 // for most classes, by default, toString() still returns the name of the class and the address where the object is // located in the memory. Usually, we want to get text information about objects in another way, so it makes sense // to override toString() for our data type. } class BerryHolder(val weight: Int) { override fun toString(): String { return weight.toString() } } open class User(val id: Int, val login: String, val email: String) { override fun toString(): String { return "User{id=$id, login=$login, email=$email}" } } fun overridingToString() { println(BerryHolder(10)) // 10 val user = User(1, "uncle_bob", "rmartin@objectmentor.com") println(user) // User{id=1, login=uncle_bob, email=rmartin@objectmentor.com} } class Author(id: Int, login: String, email: String, val books: String): User(id, login, email) fun inheritance() { // Another reason for overriding the toString() method is working with superclasses or parent classes. val user = User(1, "marys01", "mary0101@gmail.com") val author = Author(2, "srafael", "rsabatini@gmail.com", "Captain Blood: His Odyssey") println(user) // User{id=1, login=marys01, email=mary0101@gmail.com} println(author) // User{id=2, login=srafael, email=rsabatini@gmail.com} } fun main() { overridingToString() inheritance() } ############################### kotlin.oop Main.kt import oop.basic.visibility.MyClass fun main() { val myClass = MyClass() // Using the imported class val myInternalClass = oop.basic.visibility.MyInternalClass() // Using the internal class without importing } }
Editor is loading...
Leave a Comment