content: import io.activej.inject.Injector;
  import io.activej.inject.module.AbstractModule;
  
  /**
   * A module is a collection of bindings and a binding is a "recipe" of how to make an instance of certain type.
   * You simply define all these "recipes" with DSL.
   * <p>
   * - In this example you make a lambda which creates an instance of ConsoleMessageSenderImpl which is a recipe for it.
   * - Then you say that to create an instance of MessageSender you can use the ConsoleMessageSenderImpl, which is already known how to make.
   * - And then you add a recipe with a lambda for an Application instance, which depends on MessageSender, which is known to be made as a ConsoleMessageSenderImpl.
   * <p>
   * Keep in mind, that all of those "recipes" can be split into multiple modules, and you don't need to know an exact implementation of the MessageSender
   * interface, you just know that somewhere somebody provided a recipe for it. That is the whole point of the Dependency Injection paradigm.
   * <p>
   * One notable difference is that all the recipes are by default 'singletons' - meaning that
   * an object is created from the recipe at most once per its key per injector instance, cached and reused for any other recipes that depend on it.
   */
  //[START EXAMPLE]
  public final class CoreExample {
  
  	interface MessageSender {
  
  		void send(String message);
  	}
  
  	static class ConsoleMessageSenderImpl implements MessageSender {
  
  		@Override
  		public void send(String message) {
  			System.out.println("received message: " + message);
  		}
  	}
  
  	static class Application {
  		private final MessageSender sender;
  
  		Application(MessageSender sender) {
  			this.sender = sender;
  		}
  
  		void hello() {
  			sender.send("hello from application");
  		}
  	}
  
  	static class SomeModuleWithImpls extends AbstractModule {
  
  		@Override
  		protected void configure() {
  			// this module knows how to make an instance of ConsoleMessageSenderImpl
  			// no reflection yet
  			bind(ConsoleMessageSenderImpl.class).to(ConsoleMessageSenderImpl::new);
  		}
  	}
  
  	static class ApplicationModule extends AbstractModule {
  		@Override
  		protected void configure() {
  			// we teach DI how to make an application instance
  			bind(Application.class).to(Application::new, MessageSender.class);
  		}
  	}
  
  	public static void main(String[] args) {
  		Injector injector = Injector.of(new SomeModuleWithImpls(), new ApplicationModule());
  		Application application = injector.getInstance(Application.class);
  
  		application.hello();
  	}
  }
  //[END EXAMPLE]
  