Если у нас возникает необходимость написать класс, содержащий только статические методы и переменные, то: подумайте - нужен ли вам такой класс?
Если вы понимаете, что вы не пишите в процедурном стиле на Java, то давайте ответим на вопрос: зачем такой класс может быть нужен?
С помощью таких классов удобно собирать вместе общие методы работы с данными, например, как сделано в java.lang.Math
,
для методов статической генерации объектов, как в java.util.Collections
, т.е в основном - для создания классов с утилитами.
Понятно, что раз все в классе у нас статическое, то возможность создавать объекты такого класса - излишняя и не нужная.
Главное, что необходимо учесть:
- Невозможность создания объекта класса.
- Исключить возможность наследования.
И тут необходимо вспомнить, что по умолчанию, даже если мы этого не пишем, класс имеет конструктор. Если мы не объявляем констурктор явно - он будет создан автоматически.
Как запретить использование конструктора пользователем?
- Сделать класс абстрактным - использовать
abstract
. - Объявить конструктор
private
.
Рассмотрим, какой из способов подойдет нам в нашем случае:
Не делайте такой класс abstract
.
Да, мы не сможем создать объект такого класса, но надо понимать, что abstract
предназначен для другого.
Он для наследования и не надо сбивать столку этим тех, кто впоследствии будет работать с вашим кодом.
Неплохим решением проблемы будет создание одного приватного конструктора.
При этом хорошим тоном было бы еще в нем выкидывать RuntimeException
, чтобы случайно им не воспользоваться.
Почему именно RuntimeException
или один из его наследников - понятно, так как такая ошибка - это ошибка
разработчика(ошибочное создание экземпляра класса), то надо выкидывать unchecked
исключение.
Допустим, AssertionError
.
Дабы избежать случайного наследования таких классов, как наш - необходимо объявить такой класс final
.
Т.е сказать, что такой класс является финальным, полностью законченным и не предназанченным для наследования.
Пример такого класса:
/**
* Illustrate how we can create class for only static methods and fields.
* Good way.
*/
public final class ClassForStatic {
public static String st_string = "Hello";
public static void method() {
System.out.println("Static method");
}
/**
* If we create instance of class - we throw Error.
*/
private ClassForStatic() {
throw new AssertionError();
}
/**
* We have error in this method! - cause we have only private constructor,
* and if we create instance of our class - we throw error.
*/
public static ClassForStatic errorThrow() {
return new ClassForStatic();
}
}
Разобрав общий вид и то, как стоит писать подобные классы, можно рассмотреть еще и то, как улучшить классы-агрегаторы для констант.
Как мы проговорили, очень полезно бывает вынести какие-то внутренние константы в отдельный класс. Как сделать это можно - понятно, но чтобы разграничить области констант, дабы было понятнее, полезно создать еще вложенные статические классы, в которые поместить уже ваши константы.
Вынеся в такие классы данные вы разграничите логику, что всегда хорошо.
Т.е например у вас есть константы для User
, Country
и т.д, можно все запихнуть в один класс и смешать все в кучу,
а можно сделать вот так:
public class Constants {
public static final class Category {
private Category() {
throw new AssertionError();
}
public static final int NAME_LENGTH = 100;
}
public static final class Country {
private Country() {
throw new AssertionError();
}
public static final int NAME_LENGTH = 100;
}
public static final class Series {
private Series() {
throw new AssertionError();
}
public static final int COMMENT_LENGTH = 255;
}
public static final class User {
private User() {
throw new AssertionError();
}
public static final int LOGIN_LENGTH = 25;
public static final int NAME_LENGTH = 100;
}
}
Теперь вы можете обращаться к константе как:
Constants.User.NAME_LENGTH
И это намного понятнее, чем если бы все было в одном классе.