Вообще, java нас не балует синтаксическим сахаром. Лично мне это даже очень нравится — терпеть не могу выкапывать суть под горой сладкого. Из-за этого отказался от мысли перейти на Scala — побоялся диабет схватить. Но иногда хочется все-таки сделать своему компоненту такой сладенький интерфейс, чтобы пальчики оближешь. И когда я нахожу в аскетической джаве какую-нибудь карамельку — я очень радуюсь :)
Например, есть у нас такой класс-мэппер
public class Mapping{
public String map(final String arg1, final String arg2);
}
И есть какая-то реализация, скажем
SimpleImmutableMapping, и хочется сделать для его конфигурирования фабрику-билдер, типа вот такой:
public class MappingBuilder{
public MappingBuilder map( final String arg1 ){...}
public MappingBuilder with( final String arg2 ){...}
public MappingBuilder to( final String mapping ){...}
public Mapping build(){...}
}
...
final Mapping mapping = new MappingBuilder()
.map( "A" ).with( "B" ).to("ab")
.map( "C" ).with( "D" ).to("cd")
.map( "E" ).with( "F" ).to("ef")
.build();
Что мне здесь не нравится? — мне не нравится дуракоустойчивость.

Особенно мне не нравится то, что ее здесь нет, и это хорошо видно на скриншоте справа (
Да, это у меня такая цветовая схема, нет, глаза не устают, да, мне нравится, идите в жопу).
Что будет, если я вызову в этой точке
.build()? Очевидно, ничего хорошего. В лучшем случае уже во время выполнения вылетит
IllegalStateException("Can't build mapping with supplied data: first argument is not enough"). Это если я позаботился о том, как мой объект себя ведет, если его используют "неправильно". В более реальном случае будет что-нибудь вроде
NullPointerException из недр метода
.build(). В самом паршивом случае
здесь все отработает без ошибок, а веселье начнется при использовании созданного
Mapping-а где-нибудь совсем в другом месте.
Так вот, я раньше думал, что устроить в подобном Builder-е проверку времени выполнения — на яве не получится. А оказывается можно — правда так гемморойно
как и все у нас, джавистов:
public class SimpleMapping {
//код самого мэппера -- скучный и банальный
.....
//а вот дальше начинается треш и содомия
//уберите от монитора женщин и детей, и глубоко вдохните
public interface FirstArgumentGivenState {
public SecondArgumentGivenState with( final String arg2 );
}
public interface SecondArgumentGivenState {
public FullMappingGivenState to( final String mapping );
}
public interface FullMappingGivenState {
public Mapping build();
public FirstArgumentGivenState map( final String arg1 );
}
private static class Builder implements
FirstArgumentGivenState,
SecondArgumentGivenState,
FullMappingGivenState {
public FirstArgumentGivenState map( final String arg1 ) {
...
return this;
}
public SecondArgumentGivenState with( final String arg2 ) {
...
return this;
}
public FullMappingGivenState to( final String mapping ) {
...
return this;
}
public Mapping build() {
...
}
}
public static FullMappingGivenState builder() {
return new Builder();
}
}
....
final Mapping mapping = SimpleMapping.builder()
.map( "A" ).with( "B" ).to( "ab" )
.map( "C" ).with( "D" ).to( "cd" )
.map( "E" ).with( "F" ).to( "ef" )
.build();



Попробуйте теперь вызвать что-то неправильно :)
По названию интерфейсов можно понять, как эта конструкция масштабируется на более сложные системы — представляем Builder в виде конечного автомата, на каждое состояние заводим интерфейс в котором методы, соответствующие разрешенным переходам из этого состояния. И private класс-реализация реализует все эти интерфейсы.
По-правде, я не уверен, что придумать
такое было хорошей идеей — встреть я такой адъ в чужом коде, матерился бы в голос. А ведь самому-то теперь будет сложно удержаться :)
Если серьезно, то мне кажется, делать такое в каждом Builder-е это over-engeneering. Сложность понимания "как, и зачем, черт подери, такое здесь наворочено" не окупает небольшого увеличения дуракоустойчивости. Но вот если какая-нибудь изолированная, часто используемая библиотека... Тогда к странным типам возвращаемых значений у методов Builder-а можно и привыкнуть, а вот compile time safety при широком использовании может вполне себя окупить.
Если что — похороны за ваш счет. Я предупреждал :)
UPD:Как мне подсказывает Александр в комментариях — это уже описано как паттерн
Wizard. Было бы слишком оптимистично ожидать, что такую штуку еще никто не придумал. Статью рекомендую прочитать, примеры там забавные.