вторник, 21 февраля 2017 г.

Groovy для себя

* Методы классов по умолчанию публичные.
* Поля классов по умолчанию приватные.
* Поля одного типа можно писать через запятую
* Groovy по-умолчанию импортирует основные(наиболее часто используемые) Java пакеты JDK, по этому импортировать нам приходится не часто.
* самовыполняемый скрипт
Например hello.groovy файл:
#!/usr/bin/env groovy
println "Hello!"
никакой магии, просто env это не просто посмотреть переменные среды а
$ man env
ENV(1)                    BSD General Commands Manual                   ENV(1)

NAME
     env -- set environment and execute command, or print environment
...
* чтобы обойти гетер в бине, а достучаться напрямую к полю нужно применять оператор .@ 
* чтобы Groovy не генерировал для нас гетеры и сетеры нам нужно указать область видимости явно для поля


bean.@field
* мы не можем назвать файл также, как назвали внутри класс, если кроме декларации класса там будут еще и строки скрипта - причина проста, тогда груви попытается создать класс расширяющий GDK-шный класс скрипт и зафейлится потому что он не может создать два класса с одинаковым именем.
* классы пакета groovy.transform - такой себе lombook от Groovy: @EqualsAndHashCode, @Immutable, @ToString и другие.
* Элвис-оператор. Возвращает обьект до, если он не налл, и обьект после, если первый null
def elvis = obj ?: "Default"
* Оператор in в ренджах
def validAges = 18..35
def someAge = 19
assert someAge in validAges
* в java 8 простых типов + 8 обверток вокруг них и один спецтип String, и того 17

* Groovy Numbers

 -  в Java float/float=float в Groovy float/float=Double
 - в Groovy число с плавающей точкой по-умолчанию BigDecimal. Чем это хорошо? Вот пример:
println 5.0d - 4.1d
println 5.0 - 4.1
0.900000000000004
0.9
Поэтому мы не паримся по поводу этого недочета с Double, а тупо не сталкиваемся с такой проблемой.
 - деление интов. В Java Integer/Integer=Integer. Но в Groovy Integer/Integer=BigDecimal. Это чтобы 1/2 не равнялось 0.
Но можно сделать как и Java
println 1.intdiv(2)
- другие GDK методы
assert 2 == 2.5.toInteger() //conversion
assert 2 == 2.5 as Integer //enforced coercion
assert 2 == (int) 2.5 // cast

assert '5.50'.isNumber()
assert 5 == '5'.toInteger()

20.times {
  println '-'
}

1.upto(10) {num ->
  println num
}

10.downto(1) {num ->
  println num
}

0.step(1, 0.1) {num ->
  println num
}
* перегрузка операторов. В груви есть таблица мапинга операторов языка на имена методов. То есть если на обьект применяется оператор, то интепретатор поищет нет ли у класса этого обьекта метода с именем, которое мапится к этому оператору. Если есть, то будет вызван этот метод.
Операторы живут org.codehaus.groovy.runtime.DefaultGroovyMethods
class Account {
    BigDecimal balance = 0
    String type

    def deposit(BigDecimal sum) {
        balance += sum
    }

    def withdraw(BigDecimal sum) {
        balance -= sum
    }

    BigDecimal plus(Account account) {
        balance + account.balance
    }
}

Account checking = new Account(type: "Checking")
checking.deposit(100.00)
Account saving = new Account(type: "Saving")
saving.deposit(50.00)

BigDecimal total = checking + saving
println total

* Строки в GDK

- Под капотом с лету интерполяция "Value: ${value}", "two: $(1+1)"
- Чтобы вырубить - сингквоут 'Value: ${value}', 'two: $(1+1)'
- Мультилайн по тому же принципу:
"""
Value: ${value}"
"two: $(1+1)"
"""
'''
Value: ${value}"
"two: $(1+1)"
'''
- Dollar slashy - спецвозможность для отказа от екранирования
/a\b/ - если внутри только один слеш
$/C:/groovy/path/to/$ - ну и если много

* GDK RegExps

find operator (=~) - создает java.util.regex.Matcher
match operator (==~) - отвечает булином, если происходит строгое соответствие указанной регулярке
pattern operator (~string) - создает java.util.regex.Pattern

* Collections

Ranges

1..10 - groovy.lang.IntRange. Включительный диапазон ( 1 и 10 в нем)
1..<10 -="" 9="" div="">

Lists

def nums = [1,2,3,4,5,6] - java.util.ArrayList
[1,2,3,4,5,6] as LinkedList - java.util. LinkedList
[1,2,3].push(4) == [1,2,3,4]
[1,2,3].putAt(1, 4) == [1,4,2,3]
([1,2,3][0] = 4) == [4,2,3]
-----------
def nums = [1,2,3]
def extended = nums + 4
extended == [1,2,3,4]
nums == [1,2,3]
----------
def nums = [1,2,3]
nums << 4
nums == [1,2,3,4]
----------
def nums = [1,2,2,3,2]
def cutted = nums - 2
cutted == [1,3]
nums == [1,2,2,3,2]
----------
[1,2,3].pop() == [1,2]
[1,2,3].removeAt(0) == [2,3]
--------------
[1,2,3,4,5,6].getAt(0..3) == [1,2,3,4]
assert [1,2,3,4,5] == [1,[2,3],[[4]],[],5].flatten()
[1,2,3,4,4,5].unique() == [1,2,3,4,5]
--------------
[1,2,3,4,5] as Set - java.util.LinkedHashSet
[2,4,3,5,1] as SortedSet - java.util.TreeSet

Maps

def map = [:] - java.unit.LinkedHashMap
def creature = [weight: 200, mammal: true, predator: false, name: "pig"]
def complFieldNamesMap = ['Some spaced name': "value"]
def keyName = 'Some spaced name'
def complFieldNamesMap2 = [(keyName): "value"]
for( key in map.keySet() ) {
  println "$key:${map[key]}"
}

* Closures

Где и для чего используются?
- Iterators
- Callbacks
- Higher-order functions
- Specialized Control Structure
- Builders
- Resource allocation
- Threads
- DSLs
- Fluent Interfaces

Если метод принимает последним аргументом замыкание, то мы можем его размещать не в скобках аргументов, а вне скобок:)
def timesTen(num, closure) {
  closure(num * 10)
}

timesTen(10, {println it})
//but we can write as
timesTen(10) {println it}
//or more habitual form
timesTen(10) {
  println it
}
Можно заметить "ключевое слово" it
Closures may have 1...N arguments, which may be statically typed or untyped. The first parameter is available via an implicit untyped argument named it if no explicit arguments are named. If the caller does not specify any arguments, the first parameter (and, by extension, it) will be null.

{ item++ }                                             1

{ -> item++ }                                       2

{ println it }                                         3

{ it -> println it }                                4

{ name -> println name }                            5

{ String x, int y ->                                6
    println "hey ${x} the value is ${y}"
}

{ reader ->                                         7
    def line = reader.readLine()
    line.trim()
}
1A closure referencing a variable named item
2It is possible to explicitly separate closure parameters from code by adding an arrow (->)
3A closure using an implicit parameter (it)
4An alternative version where it is an explicit parameter
5In that case it is often better to use an explicit name for the parameter
6A closure accepting two typed parameters
7A closure can contain multiple statements

В замыканиях кроме ключевого слова this, можно еще использовать два поля объекта класса Closure:
this corresponds to the enclosing class where the closure is defined
owner corresponds to the enclosing object where the closure is defined, which may be either a class or a closure
delegate corresponds to a third party object where methods calls or properties are resolved whenever the receiver of the message is not defined

Делегирование для того, чтобы использовать в замыкании метод, который можно подменить контекстом. Для этого в поле delegate присваивается контекст. Но этого не достаточно, если с таким же именем существует метод в собственника замыкания(объект в котором замыкание объявлено), для этого нужно еще поменять и стратегию делегирования.
def writer = {
    append 'Dan'
    append ' lives in Cleveland'
}

def append(String s) {//(1)
    println "append() called with argument of $s"
}

StringBuffer sb = new StringBuffer()
writer.resolveStrategy = Closure.DELEGATE_FIRST//(2)
writer.delegate = sb//(3)
writer()//если не поменять стратегию по умолчанию OWNER_FIRST на указанную, то будет вызвана функция скрипта(вернее метод класса, который по умолчанию оборачивает скрипт груви и являет скрипт обьектом в jvm)

Кари в Groovy

Прелесть его в том, что можем мы предзаполнить не только последние параметры, но и первые, и даже средние по индексу, для этого есть три метода Closure
curry
rcurry
ncurry

def log = { String type, Date createdOn, String msg -> 
    println "$createdOn [$type] - $msg"
}

log("DEBUG",new Date(),"This is my first debug statement...")

def debugLog = log.curry("DEBUG")
debugLog(new Date(), "This is another debug statement...")
debugLog(new Date(), "This is one more...")

def todayDebugLog = log.curry("DEBUG",new Date())
todayDebugLog("This is today's debug msg")

// right curry
def crazyPersonLog = log.rcurry("Why am I logging this way")
crazyPersonLog("ERROR",new Date())

// index based currying
def typeMsgLog = log.ncurry(1,new Date())
typeMsgLog("ERROR","This is using ncurry...")

def errorLog = typeMsgLog.ncurry(0, "ERRORY")

errorLog("My nice try")

* Правда Groovy

В отличии от Java Groovy считает истиной и объекты, которые подходят следующим условиям:
- объекты, которые не null
- числовые значения, которые не 0
- строки, которые не пустые
- списки и мапы, которые не пустые
- если метчеру патерна подошел параметр.

* Супер свитч в Groovy

В кейси подходят и классы, и списки, и ренджы и вообще все что ожидает фантазия(практически)

* Traits

Главное их отличие от default java 8 interfaces это возможность распространять и состояния, не не только функционал.

* GroovyBeans

JavaBean -  это стандарт, который говорит, чтобы обьект считался бином, он должен
1) Все поля свои тержать приватными.
2) Иметь коструктор без аргументом.
3) Иметь гетеры и сетеры для полей.
4) Реализовывать интерфейс Serializable(он пустой - без декларации каких-либо методов, JVM просто понимает что такой объект нужно если что засерилизировать и с указанного потока попытаться десерилизировать)
Ну и Groovy помогает достичь такого намного быстрее:
@groovy.transform.ToString
class Employee implements Serializable {
    String first,last,email    
}
И как говорится готово!

Комментариев нет:

Отправить комментарий