1

Base POO com Kotlin

#Kotlin
Igor Sousa
Igor Sousa

Desde já AVISO : Não sou um expert no assunto.

Estou publicando esse artigo com o intuito de ajudar pessoas leigas, que possuem interesse ou que estão estudando o assunto e que tem alguma dificuldade em entender os conceitos.

Se Ao ler este artigo surgir alguma duvida, ou notar que algo aqui abordado está incoerente me contacte através de: <https://www.linkedin.com/in/igor-damasceno-4422aa1ba/>


Ao final do artigo estão as referencias utilizadas junto de materiais para seguir o estudo ou se aprofundar mais nos conceitos que aqui foram explanados.


1.   Classes;

Como se define? Como se usa? Para que serve?

Classe é um a responsável pela formação de um conjunto de objetos com características e comportamentos semelhantes. É a representação de algo que possui [características, ações/comportamentos e funcionalidades]

Pode ser considerada como uma forma de bolo, diversos bolos podem ser feitos utilizando essa forma. Todos são classificados como bolo e possuem “semelhanças/ características padrões” todo bolo tem recheio de algum sabor que pode variar, mas que é uma característica padrão.

Classe = forma

Uma classe Carro fornece uma “forma” padrão para produção de diversos carros. Já que todo carro essencialmente precisa ter [motor, bancos/lugares, peso, rodas e etc..]

Class é um conjunto de Características e Ações/funções que um objeto específico por padrão deve ter.

                 Uma class possui:

                 -Comportamentos = métodos

                 -Características padrões que podem variar/atributos = parâmetros/propriedades.

Class Pessoa (var nome:String, var idade: Int){...}

Esses atributos [nome, idade] são essenciais para a construção do objeto. São parâmetros essenciais para que se possa utilizar a class e sem a class não se tem o objeto.

“para (formação) de uma pessoa precisa-se de nome e idade”

Tendo essas informações, esses dados aí sim a classe será instanciada. Ou seja, será utilizado da class/forma para se criar um objeto/bolo.

Class Pessoa(var nome:String, var idade:Int){....

Var p1(objeto pessoa1): Pessoa = Pessoa(“joão”, 21)//Criou-se o objeto “p1” que tem nome “joão” e idade “21”

Instancia = Concretização de uma class, é a criação de um objeto seguindo a “forma”


 

2.   Construtores; É oque permite a construção do objeto.

Eles são os requisitos/parâmetros essenciais para que um objeto possa ser criado e para que uma class consiga ser instanciada. Sendo mais direto, construtor é o que constrói um objeto kk

Class Pessoa (var nome:String, var idade:Int) { esse é o construtor da class pessoa.

Em kotlin existem dois tipos de construtores:

-Constructores primários

O (init) representa um bloco de inicialização e pode obter mais de um bloco de código que é utilizado acessa os parâmetros do construtor primário da classe atribuindo os valores/dados dos parâmetros pra variáveis da class, mas o método init pode ser utilizado para muitas outras coisas como inicializar outros métodos ou objetos e etc..

OBS: A CLASSE NÃO PRECISA NECESSARIAMENTE, POSSUIR UM CONSTRUTOR PRIMÁRIO, ELA PODE DECLAR SUAS PROPRIEDADES NO CONSTRUTOR SECUNDÁRIO OU NO SEU CORPO. UMA CLASS PODE SER CRIADA IGNORANDO A UTILZAÇÃO DE UM CONSTRUCTOR EM SEU CABEÇAHO COMO:

Class Pessoa {...}

ISSO JÁ É SUFICENTE PARA CRIAÇÃO DE UMA CLASSE, POIS O KOTLIN POR PADRÃO IRA CRIAR UM CONSTRUCTOR DA CLASSE SEM PARAMETROS. OU SEJA :

Class Pessoa {...}

Class Pessoa1 () {..}

DÃO NO MESMO AMBAS CLASS POSSUEM UM CONSTRUTOR PRIMARIO SEM PARAMETROS/ REGRAS, ENTÃO UM OBJETO PODE SER CRIADO SEM QUE A CLASS TENHA UMA CARACTERISTICA DELE.

 

-Constructores secundário, Pode existir e estar mais de uma vez na class, desde que tenha alguma alteração em seus parâmetros e esteja referenciando/delegando devidamente o construtor primário e passando os parâmetros que o mesmo pede, como:

Class Pessoa(var nome: String){

Constructor (nome1:String, idade:Int): this (nome1)

}

Caso o constructor primário não possua parâmetros a referenciação/delegação dever ser feita da mesma forma:

Class Pessoa( ){

Constructor (nome1:String, idade:Int): this ( )

}

Caso não haja nenhum construtor primário, o construtor secundário não devera delegar/referenciar:

Class Pessoa{

Constructor (nome1:String, idade:Int)

}

O construtor secundario, ao contrario do primario que utiliza o init inicialização não necessita do init já que por padrão o constructor secundario possui inicializador só ao abrir e fechar {chaves}:

Constructor (nome1:String, idade:Int){

Println(“inicializa um print”)

This nome1 = namePessoa //inicializa a atribuição de um parâmetro do construtor para um parâmetro/atributo da class que sera armazenado na variável “namePessoa”

}

Ou seja:

 Construtores: São funções que formam a class, utilizados para iniciar e criar objetos

No Kotlin os construtores são divididos entre Primários e Secundários

 -Construtor primário:

  . Faz parte do cabeçalho class e contém parâmetros;

  . Parâmetros são as características do objeto;

  . Os parâmetros são acompanhados das palavras reservadas val ou var;

  . Estrutura básica do construtor primário: class Nome (val propriedade: Tipo){ }

  . Não pode obter nenhum código em sua estrutura;

 -Construtor secundário:

  . Pode existir mais de uma vez na class;

  . Sempre devem declarar a palavra reservada constructor, mesmo quando existir o construtor primário;

  . O construtor secundário delega o Construtor primário pela palavra this;

  . Não é necessário declarar seus parâmetros com as palavras reservadas val ou var;

Instância da Class: Instanciamento serve para invocar um construtor, aparece antes do construtor secundário e toda a clase com construtor primário ou secundário deve ser invocada;

Funções Membro: São funções declaradas dentro de classes, possuem a mesma regra das funções, com exceção da palavra this para referenciar a sua instância e não use função membro quando houver construtor secundário. São vistas também como métodos.

Nível de acesso: private class (classe privada: acesso bloqueado) -> Ex: private class Medicamento

protected class (subclasse privada: acesso bloqueado) -> protected class Tributação

OBS: Subclasse está dentro da classe, ou seja aninhada!


 

3.   Herança; É um dos principais recursos do estudo da orientação ao objeto;

O objetivo da Herança é estender ou modificar o comportamento de um código existente, a herança são características passadas de uma super classe para uma sub classe (ou classe derivada), classe existente é a que irá ceder as características e a classe criada irá herdar as características da super classe, um conjunto de classes podem partilhar as mesmas características e essas características podem formar uma super classe;

Exemplo de pensamento: Hamburguer e pizza são sub classes que pertencem a super class Food. Por que? porque a pizza e o hamburguer possuem características em comuns, tem um preço, tem calorias e tem nome. Ou seja, pertencem a mesma Super Class.

- Herança em Kotlin: A classe (local que armazena as propriedades do objeto - "forma de bolo") possui um supertipo chamado ANY que pode estar amostra ou oculto Any() é a simplificação da palavra Anyone(). A classe :Any() possui as funções equals(), hashCode() e toString() Exemplo:

1) class Food (val price: Double)

2) class Food (val price: Double): Any( )

Class 1 é igual a class 2, o Any () não as diferencia

 A função "Any()" significa que não há supertipo declarado nesta class, o que seria a mesma coisa que não indicar Any() uma class herda as propriedades de um supertipo e esta herança é declarada pela troca do Any() pelo nome da super class, para uma super class ser identificada, é necessário que esta possua a palavra open no começo de sua nomeação. Exemplo:

open class Food(val price: Double)

class Hamburguer(price: Double) : Food()

OBS: Kotlin não suporta herança MULTIPLA, apenas uma class pode ser incluída no supertipo de outra class, a class que herdar as características de uma super class ela irá perder a sua palavra reservada (val/var), a propriedade recebida na sub class pode ser usada por uma instância (função) dessa class. Exemplo:

 val hamburguer: Hamburguer = Hamburguer(2,98)

println("total: ${hamburguer.price}")

OBS: sobrescrever é mudar a propriedade.

A existência de um construtor secundário numa super class é devido a falta de um construtor primário na mesma;

Quando a Super class não possui as suas propriedades armazenadas no construtor primário, é necessário que esta declare as propriedades no construtor secundário e inicialize-os nas subclasses usando a palavra THIS;

       Exemplo: open class Food{

                   open val price:Double

                   constructor(price: Double){

                       this.price = price

                   }

               }

               class Hamburguer: Food{

                   constructor(price: Double): super(price)

               }

OBS: Super class sem construtor primário vai chamar uma class sem o construtor primário, também, para dar as suas características;

       Exemplo: open class Food {

                       open var price: Double = 0.0

                       open var name: String = ""

                       constructor(price: Double){

                       this.price = price

                       }

                       constructor(name: String){

                       this.name = name

                       }

                       constructor(price: Double, name: String){

                       this.name = name

                       this.price = price

                       }

       }

               class Hamburguer: Food{

                       constructor(price: Double): super(price)

                       constructor(name: String): super(name)

                       constructor(name: String, price: Double): super(name, price)

               }

Override: é uma palavra importante para sobrescrever uma propriedade da super class na subclass;

Lembre-se que não dá para alterar o valor de uma super class, apenas herdá-lo e mudá-lo;

Lembre-se que a palavra OPEN define a super class como sendo ela mesma!

Caso a class não tenha a palavra OPEN como inicial, ela não poderá ter herdeiros e nem serem sobrescritas;

Exemplo: open class Food(open val price: Double)

        class Hamburguer (override val price: Double): Food(price)

OVERRIDE pode estar dentro dos parênteses do constructor(OVERRIDE...) e pode estar fora, como no exemplo abaixo:

Exemplo: open class Food{

           val price: Double,

           val name: String

 

           constructor(price: Double){

               this.price = price

           }

           constructor(name: String){

               this.name = name

           }

}

       open Topping(price: Double, private val food: Food): Food(price){

                   override price: Double = price

                   get() = field + food.price

       }

OverLoad: Significa Sobrecarga, quando você tem o mesmo método sendo repetido com assinaturas diferentes, ou seja os métodos são iguais com tipos, ou parâmetros diferentes:

fun overload(i:Int) = println("OverLoad 1")

   fun overload(i:Double) = println("OverLoad 1")

PARA SER UM OVERLOAD OS METODOS TÊM QUE POSSUIR O MESMO NOME.

O kotlin consegue diferenciar os métodos pelos parâmetros.


 

4.   Polimorfismo; É a capacidade do objeto de ser referenciado de várias formas,

O polimorfismo permite que duas ou mais sub-classes diferentes que herdam da mesma super-class possam utilizar um mesmo método que se comporta de formas diferentes. Ou seja, o Método AcressimoDeSalario se comporta diferente em estagiário, ondem o acréscimo é de 200 e o mesmo método se comporta de outra forma em gerente, ondem o acréscimo passa a ser de 1.600.

Polimorfismo nos possibilita versionar o comportamento de um determinado método de acordo com a classe do objeto que for referenciado. Possibilita o método de assumir diversas formas e se comportar de diferentes maneiras.

 Exemplo:

 

interface Funcionario{

   fun calculaBonus()//método essencial

}

 

class Gerente: Funcionario{

   override fun calculaBonus() {//sobrescreve o método que foi herdado

       println("GERENTE - Acrescimo de R$: 1.600")

   }

}

 

class Estagiario: Funcionario{

   override fun calculaBonus() {//sobrescreve o método.

       println("ESTAGIARIO - Acrescimo de R$: 200")

   }

}

 

fun main(){

 

   val f1: Funcionario = Gerente()

   val f2: Funcionario = Estagiario()

 

   calculo(f1)

   calculo(f2)

 

}

//Automaticamente é definido a classificação de funcionário...

fun calculo(funcionario: Funcionario){

   funcionario.calculaBonus()

}

Polimorfismo é o versionamento do método dependendo de como o objeto se classifica


 

5.   Classes Abstratas e Interfaces;

- Classes Abstratas: Uma classe abstrata é uma classe que serve de modelo para outras classes. Ela sempre será uma superclasse genérica, e suas subclasses serão mais específicas. Além disso, ela não pode ser instanciada e pode conter ou não métodos abstratos, podendo ser implementados nas classes descendentes.

Uma classe abstrata é assim como, os seus métodos OPEN por padrão. Ou seja, os seus dados podem ser herdados e não precisa indicar a palavra OPEN atrás da class. Métodos: conjunto de ações realizadas pelo objeto.

As Classes "normais" que vão herdar as propriedades das classes abstratas terão que implementar TOOODOSSS os métodos da super class. As Classes abstratas, que herdam de outras classes abstratas, não precisam herdar TODOOOS os métodos da super class abstrata;

CONCLUSÃO: A classe abstrata é uma super class capaz de herdar suas propriedades de formas diferenciadas, como por exemplo: class abstrata herda da super classe alguns métodos são herdados class "normal" herda da super classe abstrata = TODOS os métodos são herdados. Exemplo:

  abstract class Food(open val price: Double){

               abstract fun calculate(fee: Double): Double

           }

           class Hamburguer(price: Double): Food(price)

Uma classe abstrata pode herdar propriedades de uma classe "comum";

Uma class abstrata não pode ser instanciada. Toda class que herdar uma class abstrata tem que definir os métodos abstratos da classe pai/super class

-Interfaces: É UM CONTRATO DE DECLARAÇÃO DE COMPORTAMENTO DE UM CONJUNTO DE CLASSES. É uma forma de estabelecer comportamentos a serem implementados por outras classes. A interface defini o que uma classe deve fazer, ou não. As interfaces não possuem um conjunto de métodos (ações em forma de funções), apenas declaram um conjunto de métodos para as classes seguirem. Todos os métodos são Abstratos e Públicos, para possibilitar que as classes que os herdam os implementem. A classe ao implementar-se na Interface promove a implementação de todos os seus métodos;

As Interfaces não podem ser iniciadas, ou seja, não podemos criar um objeto de interface. Possuem somente a declaração dos métodos, ou seja, o tipo de retorno, o nome do método, seus parâmetros e as exceções lançadas pelo método. A implementação dos métodos será feita pelas classes que implementam a interface.

A PRINCIPAL DIFERENÇA ENTRE CLASS ABSTRARA E INTERFACE É QUE UMA INTERFACE NÃO PERMITE PROPRIEDADES E INICIALIZADORES DE PROPRIEDADES. OU SEJA AS PROPRIEDADES EM UMA INTERFACE NUNCA TEM VALORES EX: var senha: Int SENHA NÃO POSSUI VALOR.

CLASSE X INTERFACE: A classe pode herdar apenas uma classe, mas pode implantar mais de uma Interface. UMA CLASSE NÃO PODE HERDAR MAIS DE UMA CLASSE ABSTRATA OU SEJA QUALQUER OUTRO TIPO DE SUPERCLASS. MAS UMA CLASSE PODE HERDAR DIVERSAS INTERFACES.

Exemplo de Uso de Interface: Suponha que temos um sistema com um módulo de cadastros onde temos diversos cadastros ( cadastro de clientes, cadastro de fornecedores, cadastros de produtos, cadastro de pedidos, cadastro de pagamentos, etc. ). São muitos cadastros e todos devem permitir incluir, alterar, excluir e consultar informações.

Exemplo de Interface:

interface A {

   fun foo() { print("A") }

   fun bar()

}

interface B {

   fun foo() { print("B") }

   fun bar() { print("bar") }

}

class C : A {

   override fun bar() { print("bar") }

}

class D : A, B {

   override fun foo() {

       super<A>.foo()

       super<B>.foo()

   }

   override fun bar() {

       super<B>.bar()

   }

}


 

6.   Classes de Dados;

São Classes que armazenam apenas Dados, e não possuem necessariamente a necessidade de comportamentos. O que é uma Data Class?

  R: É um conjunto de dados, que quando armazenados em uma memória, jamais poderão ser modificados, apenas copiados.

As Data class são totalmente dependentes de seus dados, são estruturas semelhantes as classes que já conhecemos;

       data class Nome (val variavel: Tipo) { Bloco de código}

A Data Class é obrigada a possuir uma propriedade, ou seja, um construtor primário e pelo menos um parâmetro.

A Data Class pode ser copiada e modificada, da seguinte maneira:

      data class User (val name: String, val age:Int){

       }

      fun copy(name: String = this.name, age: Int = this.age) = User (name, age){

      val jane = User("Jane", 35)

      val (name, age) = jane

      println("$name tem $age anos.")

      }


7.   Propriedades;

Declarando propriedades: As propriedades nas classes Kotlin podem ser declaradas como mutáveis ​​usando a var palavra - chave

ou como somente leitura usando a val palavra - chave.

class Address {

   var name: String = "Holmes, Sherlock"

   var street: String = "Baker"

   var city: String = "London"

}

Para usar uma propriedade, basta referir-se a ela pelo nome:

fun copyAddress (address: Address): Address {

   val result = Address() // criar um objeto resultado da super class address

   result.name = address.name // chama name acessando a class.

   result.street = address.street

   // ...

   return result

}

Getters e setters: A sintaxe completa para declarar uma propriedade é a seguinte.

var <propertyName>[: <PropertyType>] [= <property_initializer>]

[<getter>]

[<setter>]

O inicializador, getter e setter são opcionais. O tipo de propriedade é opcional se puder ser inferido do inicializador (ou do tipo de retorno getter, conforme mostrado abaixo).

var initialized = 1 // tem tipo Int, getter e setter padrão, já que é mutavel

// var allByDefault //ERRO: inicializador explícito necessário(valor inicial), getter e setter padrão implícitos

A sintaxe completa de uma declaração de propriedade somente leitura difere de uma mutável de duas maneiras: ela começa com em val vez de var e não permite um setter:

Var : É mutável, ou seja pode mudar então possui get(pegar) e set(setar). Onde pega o valo e seta o valor possibilitando alteração.

Val: É imutável, ou seja só pode ser exibido, não pode ser alterado, por padrão só possui o get (pegar).

val simple: Int? // tem tipo Int, getter padrão, deve ser inicializado no construtor

val inferredType = 1 // tem tipo Int e um getter padrão

OBS: Você pode definir assessores personalizados para uma propriedade. Se você definir um getter personalizado, ele será chamado toda vez que você acessar a propriedade (dessa forma, você pode implementar uma propriedade computada). Aqui está um exemplo de getter personalizado:

val isEmpty: Boolean

   get() = this.size == 0

Se você definir um configurador personalizado, ele será chamado sempre que você atribuir um valor à propriedade, exceto sua inicialização. Um configurador personalizado tem a seguinte aparência:

var stringRepresentation: String

   get() = this.toString()

   set(value) {

       setDataFromString(value) //analisa a string e atribui valores a outras propriedades   }

 

Você pode omitir o tipo de propriedade se ele puder ser inferido do getter:

val isEmpty get() = this.size == 0 // é do tipo Boolean

Se você precisar alterar a visibilidade de um acessador ou anotá-lo, mas não precisar alterar a implementação padrão, poderá definir o acessor sem definir seu corpo:

var setterVisibility: String = "abc"

   private set // o setter é privado e tem a implementação padrão

var setterWithAnnotation: Any? = null

   @Inject set //anotar o setter com Inject

Campos de apoio;

No Kotlin, um campo é usado apenas como parte de uma propriedade para manter seu valor na memória. Os campos não podem ser declarados diretamente. No entanto, quando uma propriedade precisa de um campo de apoio, o Kotlin o fornece automaticamente. Este campo de apoio pode ser referenciado nos acossadores usando o field identificador:

Field: A sintaxe completa para declarar uma propriedade é a seguinte. Uma variável dentro da classe e fora do construtor pode ser acessada sim se ela não for privada. Por exemplo:

class Maquina (var marca: String) {

var modelo: String = ""

}

fun main() {

val m = Maquina("xpto")

m.modelo = "abc"

}

A palavra field entra em ação quando nós precisamos definir getter e setter específicos para aquela variável. Em alguns casos, é necessário implementar alguma lógica antes que o valor seja atribuído.

Nesses casos, a variável não pode ser referenciada simplesmente pelo seu nome, ou temos uma repetição de chamadas infinita para ela mesma. Aí entra a palavra field que diz para o compilador impedir esse procedimento.

Ou seja, caso o parâmetro não possua um valor inicializado o get e o set não fiquem procurando esse valor.

Propriedades de apoio;

Se você quiser fazer algo que não se encaixa neste esquema de campo de apoio implícito , você sempre pode voltar a ter uma propriedade de apoio :

private var _table: Map<String, Int>? = null

public val table: Map<String, Int>

   get() {

       if (_table == null) {

           _table = HashMap() //parâmetros de tipo são inferidos

       }

       return _table ?: throw AssertionError("Definido como nulo por outro tópico ")

   }

OBS: Na JVM: o acesso a propriedades privadas com getters e setters padrão é otimizado para evitar sobrecarga de chamada de função.

Constantes de tempo de compilação; Se o valor de uma propriedade somente leitura for conhecido no tempo de compilação, marque-o como uma constante de tempo de compilação usando o const modificador. Essas propriedades precisam cumprir os seguintes requisitos:

Nível superior ou membro de uma object declaração ou um objeto complementar .

Inicializado com um valor de tipo String ou um tipo primitivo

Sem getter personalizado

Essas propriedades podem ser usadas em anotações:

const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"

@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }


 

8.   Referencias;

 

GUERRA, Ana. Base teórica para o Kotlin / Programação Orientada a Objetos.

Disponível em:<https://github.com/GuerraAna/Theoretical.Basis.Kotlin>.

Acesso em : 26 de junho de 2021

 

POO: Os 4 pilares da Programação Orientada a Objetos. Devmedia,

Disponível em:< https://tecnoblog.net/247956/referencia-site-abnt-artigos>

Acesso em : 24 de junho de 2021

 

Classes. Kotlinang,

Disponível em: <https://kotlinlang.org/docs/classes.html#creating-instances-of-classes>

Acesso em : 25 de junho de 2021

 

Kotlin a Partir do Princípio: Classes e Objetos. Evantotuts+,

Disponível em: < https://code.tutsplus.com/pt/tutorials/kotlin-from-scratch-classes-and-objects--cms-29590>

Acesso em : 23 de junho de 2021

 

1
48

Comentários (1)

1
Vagner Bellacosa

Vagner Bellacosa

06/08/2021 19:29

Igor, ficou muito bacana, parabens pelo trabalho

Amante de Tecnologia & Desenvolvimento Android Nativo

Brasil