diff --git a/core-groovy/build.gradle b/core-groovy/build.gradle
new file mode 100644
index 0000000000..b3f33836da
--- /dev/null
+++ b/core-groovy/build.gradle
@@ -0,0 +1,13 @@
+group 'com.baeldung'
+version '1.0-SNAPSHOT'
+
+apply plugin: 'groovy'
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ compile 'org.codehaus.groovy:groovy-all:2.5.0-alpha-1'
+ testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
+}
diff --git a/core-groovy/pom.xml b/core-groovy/pom.xml
index 965670e78e..eb9932e0cf 100644
--- a/core-groovy/pom.xml
+++ b/core-groovy/pom.xml
@@ -24,12 +24,17 @@
org.codehaus.groovy
groovy
- 2.4.13
+ 2.5.0-alpha-1
+
+
+ org.codehaus.groovy
+ groovy-all
+ 2.5.0-alpha-1
org.codehaus.groovy
groovy-sql
- 2.4.13
+ 2.5.0-alpha-1
org.junit.jupiter
@@ -49,6 +54,12 @@
2.4.0
test
+
+ org.spockframework
+ spock-core
+ 1.1-groovy-2.4
+ test
+
@@ -114,5 +125,5 @@
4.12.0
4.12
-
+
diff --git a/core-groovy/src/main/groovy/com/baeldung/json/Account.groovy b/core-groovy/src/main/groovy/com/baeldung/json/Account.groovy
new file mode 100644
index 0000000000..84b294f0bd
--- /dev/null
+++ b/core-groovy/src/main/groovy/com/baeldung/json/Account.groovy
@@ -0,0 +1,7 @@
+package com.baeldung.json
+
+class Account {
+ String id
+ BigDecimal value
+ Date createdAt
+}
\ No newline at end of file
diff --git a/core-groovy/src/main/groovy/com/baeldung/json/JsonParser.groovy b/core-groovy/src/main/groovy/com/baeldung/json/JsonParser.groovy
new file mode 100644
index 0000000000..0d7c451972
--- /dev/null
+++ b/core-groovy/src/main/groovy/com/baeldung/json/JsonParser.groovy
@@ -0,0 +1,36 @@
+package com.baeldung.json
+
+import groovy.json.JsonGenerator
+import groovy.json.JsonOutput
+import groovy.json.JsonParserType
+import groovy.json.JsonSlurper
+
+class JsonParser {
+
+ Account toObject(String json) {
+ JsonSlurper jsonSlurper = new JsonSlurper()
+ jsonSlurper.parseText(json) as Account
+ }
+
+ Account toObjectWithIndexOverlay(String json) {
+ JsonSlurper jsonSlurper = new JsonSlurper(type: JsonParserType.INDEX_OVERLAY)
+ jsonSlurper.parseText(json) as Account
+ }
+
+ String toJson(Account account) {
+ JsonOutput.toJson(account)
+ }
+
+ String toJson(Account account, String dateFormat, String... fieldsToExclude) {
+ JsonGenerator generator = new JsonGenerator.Options()
+ .dateFormat(dateFormat)
+ .excludeFieldsByName(fieldsToExclude)
+ .build()
+ generator.toJson(account)
+ }
+
+ String prettyfy(String json) {
+ JsonOutput.prettyPrint(json)
+ }
+
+}
diff --git a/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy b/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy
new file mode 100644
index 0000000000..fcd51d58bc
--- /dev/null
+++ b/core-groovy/src/test/groovy/com/baeldung/json/JsonParserTest.groovy
@@ -0,0 +1,80 @@
+package com.baeldung.json
+
+import spock.lang.Specification
+
+import java.text.SimpleDateFormat
+
+class JsonParserTest extends Specification {
+
+ JsonParser jsonParser
+
+ void setup () {
+ jsonParser = new JsonParser()
+ }
+
+ def 'Should parse to Account given Json String' () {
+ given:
+ def json = '{"id":"1234","value":15.6}'
+ when:
+ def account = jsonParser.toObject(json)
+ then:
+ account
+ account instanceof Account
+ account.id == '1234'
+ account.value == 15.6
+ }
+
+ def 'Should parse to Account given Json String with date property' () {
+ given:
+ def json = '{"id":"1234","value":15.6,"createdAt":"2018-01-01T00:00:00+0000"}'
+ when:
+ def account = jsonParser.toObjectWithIndexOverlay(json)
+ then:
+ account
+ account instanceof Account
+ account.id == '1234'
+ account.value == 15.6
+ println account.createdAt
+ account.createdAt == Date.parse('yyyy-MM-dd', '2018-01-01')
+ }
+
+ def 'Should parse to Json given an Account object' () {
+ given:
+ Account account = new Account(
+ id: '123',
+ value: 15.6,
+ createdAt: new SimpleDateFormat('MM/dd/yyyy').parse('01/01/2018')
+ )
+ when:
+ def json = jsonParser.toJson(account)
+ then:
+ json
+ json == '{"value":15.6,"createdAt":"2018-01-01T00:00:00+0000","id":"123"}'
+ }
+
+ def 'Should parse to Json given an Account object, a date format and fields to exclude' () {
+ given:
+ Account account = new Account(
+ id: '123',
+ value: 15.6,
+ createdAt: new SimpleDateFormat('MM/dd/yyyy').parse('01/01/2018')
+ )
+ when:
+ def json = jsonParser.toJson(account, 'MM/dd/yyyy', 'value')
+ then:
+ json
+ json == '{"createdAt":"01/01/2018","id":"123"}'
+ }
+
+ def 'Should prettify given a json string' () {
+ given:
+ String json = '{"value":15.6,"createdAt":"01/01/2018","id":"123456"}'
+ when:
+ def jsonPretty = jsonParser.prettyfy(json)
+ then:
+ jsonPretty
+ jsonPretty == '{\n "value": 15.6,\n "createdAt": "01/01/2018",\n "id": "123456"\n}'
+ }
+
+
+}
diff --git a/core-java-9/src/main/java/com/baeldung/java9/methodhandles/Book.java b/core-java-9/src/main/java/com/baeldung/java9/methodhandles/Book.java
new file mode 100644
index 0000000000..479f62cb4e
--- /dev/null
+++ b/core-java-9/src/main/java/com/baeldung/java9/methodhandles/Book.java
@@ -0,0 +1,17 @@
+package com.baeldung.java9.methodhandles;
+
+public class Book {
+
+ String id;
+ String title;
+
+ public Book(String id, String title) {
+ this.id = id;
+ this.title = title;
+ }
+
+ @SuppressWarnings("unused")
+ private String formatBook() {
+ return id + " > " + title;
+ }
+}
diff --git a/core-java-9/src/test/java/com/baeldung/java9/methodhandles/MethodHandlesTest.java b/core-java-9/src/test/java/com/baeldung/java9/methodhandles/MethodHandlesTest.java
new file mode 100644
index 0000000000..7646755358
--- /dev/null
+++ b/core-java-9/src/test/java/com/baeldung/java9/methodhandles/MethodHandlesTest.java
@@ -0,0 +1,152 @@
+package com.baeldung.java9.methodhandles;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.*;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * Test case for the {@link MethodHandles} API
+ */
+public class MethodHandlesTest {
+
+ @Test
+ public void givenConcatMethodHandle_whenInvoked_thenCorrectlyConcatenated() throws Throwable {
+
+ MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
+ MethodType mt = MethodType.methodType(String.class, String.class);
+ MethodHandle concatMH = publicLookup.findVirtual(String.class, "concat", mt);
+
+ String output = (String) concatMH.invoke("Effective ", "Java");
+
+ assertEquals("Effective Java", output);
+ }
+
+ @Test
+ public void givenAsListMethodHandle_whenInvokingWithArguments_thenCorrectlyInvoked() throws Throwable {
+ MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
+ MethodType mt = MethodType.methodType(List.class, Object[].class);
+ MethodHandle asListMH = publicLookup.findStatic(Arrays.class, "asList", mt);
+
+ List list = (List) asListMH.invokeWithArguments(1, 2);
+
+ assertThat(Arrays.asList(1, 2), is(list));
+ }
+
+ @Test
+ public void givenConstructorMethodHandle_whenInvoked_thenObjectCreatedCorrectly() throws Throwable {
+ MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
+ MethodType mt = MethodType.methodType(void.class, String.class);
+ MethodHandle newIntegerMH = publicLookup.findConstructor(Integer.class, mt);
+
+ Integer integer = (Integer) newIntegerMH.invoke("1");
+
+ assertEquals(1, integer.intValue());
+ }
+
+ @Test
+ public void givenAFieldWithoutGetter_whenCreatingAGetter_thenCorrectlyInvoked() throws Throwable {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ MethodHandle getTitleMH = lookup.findGetter(Book.class, "title", String.class);
+
+ Book book = new Book("ISBN-1234", "Effective Java");
+
+ assertEquals("Effective Java", getTitleMH.invoke(book));
+ }
+
+ @Test
+ public void givenPrivateMethod_whenCreatingItsMethodHandle_thenCorrectlyInvoked() throws Throwable {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ Method formatBookMethod = Book.class.getDeclaredMethod("formatBook");
+ formatBookMethod.setAccessible(true);
+
+ MethodHandle formatBookMH = lookup.unreflect(formatBookMethod);
+
+ Book book = new Book("ISBN-123", "Java in Action");
+
+ assertEquals("ISBN-123 > Java in Action", formatBookMH.invoke(book));
+ }
+
+ @Test
+ public void givenReplaceMethod_whenUsingReflectionAndInvoked_thenCorrectlyReplaced() throws Throwable {
+ Method replaceMethod = String.class.getMethod("replace", char.class, char.class);
+
+ String string = (String) replaceMethod.invoke("jovo", 'o', 'a');
+
+ assertEquals("java", string);
+ }
+
+ @Test
+ public void givenReplaceMethodHandle_whenInvoked_thenCorrectlyReplaced() throws Throwable {
+ MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
+ MethodType mt = MethodType.methodType(String.class, char.class, char.class);
+ MethodHandle replaceMH = publicLookup.findVirtual(String.class, "replace", mt);
+
+ String replacedString = (String) replaceMH.invoke("jovo", Character.valueOf('o'), 'a');
+
+ assertEquals("java", replacedString);
+ }
+
+ @Test
+ public void givenReplaceMethodHandle_whenInvokingExact_thenCorrectlyReplaced() throws Throwable {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ MethodType mt = MethodType.methodType(String.class, char.class, char.class);
+ MethodHandle replaceMH = lookup.findVirtual(String.class, "replace", mt);
+
+ String s = (String) replaceMH.invokeExact("jovo", 'o', 'a');
+
+ assertEquals("java", s);
+ }
+
+ @Test
+ public void givenSumMethodHandle_whenInvokingExact_thenSumIsCorrect() throws Throwable {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ MethodType mt = MethodType.methodType(int.class, int.class, int.class);
+ MethodHandle sumMH = lookup.findStatic(Integer.class, "sum", mt);
+
+ int sum = (int) sumMH.invokeExact(1, 11);
+
+ assertEquals(12, sum);
+ }
+
+ @Test(expected = WrongMethodTypeException.class)
+ public void givenSumMethodHandleAndIncompatibleArguments_whenInvokingExact_thenException() throws Throwable {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ MethodType mt = MethodType.methodType(int.class, int.class, int.class);
+ MethodHandle sumMH = lookup.findStatic(Integer.class, "sum", mt);
+
+ sumMH.invokeExact(Integer.valueOf(1), 11);
+ }
+
+ @Test
+ public void givenSpreadedEqualsMethodHandle_whenInvokedOnArray_thenCorrectlyEvaluated() throws Throwable {
+ MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
+ MethodType mt = MethodType.methodType(boolean.class, Object.class);
+ MethodHandle equalsMH = publicLookup.findVirtual(String.class, "equals", mt);
+
+ MethodHandle methodHandle = equalsMH.asSpreader(Object[].class, 2);
+
+ assertTrue((boolean) methodHandle.invoke(new Object[] { "java", "java" }));
+ assertFalse((boolean) methodHandle.invoke(new Object[] { "java", "jova" }));
+ }
+
+ @Test
+ public void givenConcatMethodHandle_whenBindToAString_thenCorrectlyConcatenated() throws Throwable {
+ MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
+ MethodType mt = MethodType.methodType(String.class, String.class);
+ MethodHandle concatMH = publicLookup.findVirtual(String.class, "concat", mt);
+
+ MethodHandle bindedConcatMH = concatMH.bindTo("Hello ");
+
+ assertEquals("Hello World!", bindedConcatMH.invoke("World!"));
+ }
+}
diff --git a/core-java/README.md b/core-java/README.md
index b160d2271e..5be4e12592 100644
--- a/core-java/README.md
+++ b/core-java/README.md
@@ -128,3 +128,4 @@
- [Recursion In Java](http://www.baeldung.com/java-recursion)
- [A Guide to the finalize Method in Java](http://www.baeldung.com/java-finalize)
- [Compiling Java *.class Files with javac](http://www.baeldung.com/javac)
+- [Method Overloading and Overriding in Java](http://www.baeldung.com/java-method-overload-override)
diff --git a/core-kotlin/pom.xml b/core-kotlin/pom.xml
index 33bdbf719f..36298ca084 100644
--- a/core-kotlin/pom.xml
+++ b/core-kotlin/pom.xml
@@ -76,7 +76,18 @@
mockito-kotlin
${mockito-kotlin.version}
test
-
+
+
+ com.github.salomonbrys.kodein
+ kodein
+ ${kodein.version}
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
@@ -189,11 +200,13 @@
1.1.2
0.15
1.5.0
+ 4.1.0
5.0.0
1.0.0
4.12.0
4.12
+ 3.9.1
diff --git a/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Controller.kt b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Controller.kt
new file mode 100644
index 0000000000..721bdb04bc
--- /dev/null
+++ b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Controller.kt
@@ -0,0 +1,3 @@
+package com.baeldung.kotlin.kodein
+
+class Controller(private val service : Service)
\ No newline at end of file
diff --git a/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Dao.kt b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Dao.kt
new file mode 100644
index 0000000000..a0be7ef0e0
--- /dev/null
+++ b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Dao.kt
@@ -0,0 +1,3 @@
+package com.baeldung.kotlin.kodein
+
+interface Dao
\ No newline at end of file
diff --git a/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/JdbcDao.kt b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/JdbcDao.kt
new file mode 100644
index 0000000000..0a09b95dbf
--- /dev/null
+++ b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/JdbcDao.kt
@@ -0,0 +1,3 @@
+package com.baeldung.kotlin.kodein
+
+class JdbcDao : Dao
\ No newline at end of file
diff --git a/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/MongoDao.kt b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/MongoDao.kt
new file mode 100644
index 0000000000..06436fcd21
--- /dev/null
+++ b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/MongoDao.kt
@@ -0,0 +1,3 @@
+package com.baeldung.kotlin.kodein
+
+class MongoDao : Dao
\ No newline at end of file
diff --git a/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Service.kt b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Service.kt
new file mode 100644
index 0000000000..bb24a5cc21
--- /dev/null
+++ b/core-kotlin/src/main/kotlin/com/baeldung/kotlin/kodein/Service.kt
@@ -0,0 +1,3 @@
+package com.baeldung.kotlin.kodein
+
+class Service(private val dao: Dao, private val tag: String)
\ No newline at end of file
diff --git a/core-kotlin/src/test/kotlin/com/baeldung/kotlin/kodein/KodeinUnitTest.kt b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/kodein/KodeinUnitTest.kt
new file mode 100644
index 0000000000..7776eebd52
--- /dev/null
+++ b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/kodein/KodeinUnitTest.kt
@@ -0,0 +1,191 @@
+package com.baeldung.kotlin.kodein
+
+import com.github.salomonbrys.kodein.*
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.Test
+
+class KodeinUnitTest {
+
+ class InMemoryDao : Dao
+
+ @Test
+ fun whenSingletonBinding_thenSingleInstanceIsCreated() {
+ var created = false
+ val kodein = Kodein {
+ bind() with singleton {
+ created = true
+ MongoDao()
+ }
+ }
+
+ assertThat(created).isFalse()
+
+ val dao1: Dao = kodein.instance()
+
+ assertThat(created).isTrue()
+
+ val dao2: Dao = kodein.instance()
+
+ assertThat(dao1).isSameAs(dao2)
+ }
+
+ @Test
+ fun whenFactoryBinding_thenNewInstanceIsCreated() {
+ val kodein = Kodein {
+ bind() with singleton { MongoDao() }
+ bind() with factory { tag: String -> Service(instance(), tag) }
+ }
+ val service1: Service = kodein.with("myTag").instance()
+ val service2: Service = kodein.with("myTag").instance()
+
+ assertThat(service1).isNotSameAs(service2)
+ }
+
+ @Test
+ fun whenProviderBinding_thenNewInstanceIsCreated() {
+ val kodein = Kodein {
+ bind() with provider { MongoDao() }
+ }
+ val dao1: Dao = kodein.instance()
+ val dao2: Dao = kodein.instance()
+
+ assertThat(dao1).isNotSameAs(dao2)
+ }
+
+ @Test
+ fun whenTaggedBinding_thenMultipleInstancesOfSameTypeCanBeRegistered() {
+ val kodein = Kodein {
+ bind("dao1") with singleton { MongoDao() }
+ bind("dao2") with singleton { MongoDao() }
+ }
+ val dao1: Dao = kodein.instance("dao1")
+ val dao2: Dao = kodein.instance("dao2")
+
+ assertThat(dao1).isNotSameAs(dao2)
+ }
+
+ @Test
+ fun whenEagerSingletonBinding_thenCreationIsEager() {
+ var created = false
+ val kodein = Kodein {
+ bind() with eagerSingleton {
+ created = true
+ MongoDao()
+ }
+ }
+
+ assertThat(created).isTrue()
+ val dao1: Dao = kodein.instance()
+ val dao2: Dao = kodein.instance()
+
+ assertThat(dao1).isSameAs(dao2)
+ }
+
+ @Test
+ fun whenMultitonBinding_thenInstancesAreReused() {
+ val kodein = Kodein {
+ bind() with singleton { MongoDao() }
+ bind() with multiton { tag: String -> Service(instance(), tag) }
+ }
+ val service1: Service = kodein.with("myTag").instance()
+ val service2: Service = kodein.with("myTag").instance()
+
+ assertThat(service1).isSameAs(service2)
+ }
+
+ @Test
+ fun whenInstanceBinding_thenItIsReused() {
+ val dao = MongoDao()
+ val kodein = Kodein {
+ bind() with instance(dao)
+ }
+ val fromContainer: Dao = kodein.instance()
+
+ assertThat(dao).isSameAs(fromContainer)
+ }
+
+ @Test
+ fun whenConstantBinding_thenItIsAvailable() {
+ val kodein = Kodein {
+ constant("magic") with 42
+ }
+ val fromContainer: Int = kodein.instance("magic")
+
+ assertThat(fromContainer).isEqualTo(42)
+ }
+
+ @Test
+ fun whenUsingModules_thenTransitiveDependenciesAreSuccessfullyResolved() {
+ val jdbcModule = Kodein.Module {
+ bind() with singleton { JdbcDao() }
+ }
+ val kodein = Kodein {
+ import(jdbcModule)
+ bind() with singleton { Controller(instance()) }
+ bind() with singleton { Service(instance(), "myService") }
+ }
+
+ val dao: Dao = kodein.instance()
+ assertThat(dao).isInstanceOf(JdbcDao::class.java)
+ }
+
+ @Test
+ fun whenComposition_thenBeansAreReUsed() {
+ val persistenceContainer = Kodein {
+ bind() with singleton { MongoDao() }
+ }
+ val serviceContainer = Kodein {
+ extend(persistenceContainer)
+ bind() with singleton { Service(instance(), "myService") }
+ }
+ val fromPersistence: Dao = persistenceContainer.instance()
+ val fromService: Dao = serviceContainer.instance()
+
+ assertThat(fromPersistence).isSameAs(fromService)
+ }
+
+ @Test
+ fun whenOverriding_thenRightBeanIsUsed() {
+ val commonModule = Kodein.Module {
+ bind() with singleton { MongoDao() }
+ bind() with singleton { Service(instance(), "myService") }
+ }
+ val testContainer = Kodein {
+ import(commonModule)
+ bind(overrides = true) with singleton { InMemoryDao() }
+ }
+ val dao: Dao = testContainer.instance()
+
+ assertThat(dao).isInstanceOf(InMemoryDao::class.java)
+ }
+
+ @Test
+ fun whenMultiBinding_thenWorks() {
+ val kodein = Kodein {
+ bind() from setBinding()
+ bind().inSet() with singleton { MongoDao() }
+ bind().inSet() with singleton { JdbcDao() }
+ }
+ val daos: Set = kodein.instance()
+
+ assertThat(daos.map { it.javaClass as Class<*> }).containsOnly(MongoDao::class.java, JdbcDao::class.java)
+ }
+
+ @Test
+ fun whenInjector_thenWorks() {
+ class Controller2 {
+ private val injector = KodeinInjector()
+ val service: Service by injector.instance()
+ fun injectDependencies(kodein: Kodein) = injector.inject(kodein)
+ }
+
+ val kodein = Kodein {
+ bind() with singleton { MongoDao() }
+ bind() with singleton { Service(instance(), "myService") }
+ }
+ val controller = Controller2()
+ controller.injectDependencies(kodein)
+
+ assertThat(controller.service).isNotNull
+ }
+}
\ No newline at end of file