Static Import
In order to access static members, it is necessary to qualify references with the class they came from. For example, one must say:
double r = Math.cos(Math.PI * theta);
In order to get around this, people sometimes put static members into an interface and inherit from that interface. This is a bad idea. In fact, it’s such a bad idea that there’s a name for it: the Constant Interface Antipattern (see Effective Java Item 17). The problem is that a class’s use of the static members of another class is a mere implementation detail. When a class implements an interface, it becomes part of the class’s public API. Implementation details should not leak into public APIs.
The static import construct allows unqualified access to static members without inheriting from the type containing the static members. Instead, the program imports the members, either individually:
import static java.lang.Math.PI;
import static java.lang.Math.*;
The static import declaration is analogous to the normal import declaration. Where the normal import declaration imports classes from packages, allowing them to be used without package qualification, the static import declaration imports static members from classes, allowing them to be used without class qualification.
So when should you use static import? Very sparingly! Only use it when you’d otherwise be tempted to declare local copies of constants, or to abuse inheritance (the Constant Interface Antipattern). In other words, use it when you require frequent access to static members from one or two classes. If you overuse the static import feature, it can make your program unreadable and unmaintainable, polluting its namespace with all the static members you import. Readers of your code (including you, a few months after you wrote it) will not know which class a static member comes from. Importing all of the static members from a class can be particularly harmful to readability; if you need only one or two members, import them individually. Used appropriately, static import can make your program more readable, by removing the boilerplate of repetition of class names.
Пакеты классов, package
Пакет package позволяет логически объединить классы в наборы. Основные классы java входят в пакет java.lang. Различные вспомогательные классы располагаются в пакете в java.util. Классы для ввода и вывода входят в пакет java.io, а классы для работы в сети – в java.net. Некоторые их этих пакетов содержат подпакеты. Так, например, java.lang содержит два специализированных пакета java.lang.reflect и java.lang.ref, а java.util содержит подпакет java.util.zip, который включает классы для работы с ZIPархивами.
Каждый класс имеет как простое имя, данное ему в определении, так и полное имя, включающее имя пакета, в который он входит. Например, класс String является частью пакета java.lang, а его полное имя – java.lang.String.
Структура пакетов в точности отображает структуру файловой системы. Все файлы с исходными кодами (java-класс) и байт-кодами (расширением class), образующие один пакет, хранятся в одном каталоге файловой системы. Подпакеты образуют подкаталоги этого каталога. Каждый пакет создает единое пространство имен namespace. Это означает, что все имена классов и интерфейсов в пакете должны быть уникальны. Имена в разных пакетах могут совпадать, но это будут разные программные модули. Организация классов в виде пакетов позволяет избежать конфликта имен между классами. В пакете дублирование имен классов не допускается. Принадлежность класса к пакету позволяет гарантировать однозначность имен.
Чтобы указать, что класс принадлежит определенному пакету, следует использовать директиву package, после которой указывается наименование (путь) пакета :
package company.common; public class HelloWorld < public static void main(String[] args)< System.out.println ("Привет, мир!"); >>
В данном примере класс HelloWorld располагается в пакете company.common. Физически это директория «$/company/common». При создании класса в среде разработки IDE (например, Eclipse) следует указать наименование пакета, тогда IDE самостоятельно при необходимости создаст каталог на жестком диске и разместит новый класс в этом каталоге.
Можно package в классе не определять. В этом случае класс будет находиться в пакете по умолчанию, который не имеет имени «$», т.е. класс будет располагаться в корневой директории исходных кодов проекта.
Наименование пакета может быть любым, но необходимо соблюдать его уникальность в проекте. Соглашение «Code Conventions» рекомендует записывать имена пакетов строчными буквами. Тогда они не будут совпадать с именами классов, которые, по соглашению, начинаются с прописной буквы.
Стандартная библиотека Java API включает сотни классов. Каждый программист в ходе работы создает десятки своих классов. Множество классов быстро увеличивается. Java позволяет отдельные классы, решающие определенную задачу (или несколько задач), объединять в библиотеки классов в виде архивов jar. Но эти библиотеки классов, кроме стандартных, не являются частью языка java.
Импорт пакетов и классов, import
Для использования класса в приложении, его следует подключить. Так расположенный в пакете java.util класс Scanner можно подключить следующим способом :
java.util.Scanner in = new java.util.Scanner(System.in);
В этом примере при определении/создании нового объекта был указыван пакет (полный путь к файлу). Однако данный подход не всегда удобен, и в качестве альтернативы можно импортировать пакеты и классы в приложение с помощью директивы import, которая указывается после директивы package :
package company.common; import java.util.Scanner; public class HelloWorld < public static void main(String[] args)< Scanner in = new Scanner(System.in); >>
Директива import указывается в самом начале кода, после чего идет имя подключаемого класса (класс Scanner в примере).
В примере был подключен только один класс. Однако пакет java.util содержит большое количество разных классов. И чтобы не подключать по отдельности каждый класс, можно сразу подключить весь пакет :
import java.util.*; // импорт всех классов из пакета java.util
Теперь можно использовать любой класс из пакета java.util.
Возможна ситуация, когда используется два класса с одинаковым наименованием, но из разных пакетов. Это относится, например, к классам Date, которые имеются в пакете java.util и в пакете java.sql, или классам List пакетов java.util и java.awt. И если необходимо одновременно использовать оба эти класса, то необходимо указывать полный путь к классам в пакете :
java.util.Date udate = new java.util.Date(); java.sql.Date sdate = new java.sql.Date();
Следует сказать, что основные классы из пакета java.lang (например, String) подключаются автоматически и не требуют «импортирования».
Статический импорт классов, import static
В java можно использовать статический импорт. Для этого вместе с директивой import используется модификатор static :
package company.common; import static java.lang.Math.*; import static java.lang.System.*; public class HelloWorld < public static void main(String[] args) < double result = sqrt(20); out.println(result); >>
В примере определяется статический импорт классов System и Math, которые имеют статические методы. Определение статического импорта позволяет использовать статические методы без названия класса. В примере статическая функция sqrt(20) (можно и Math.sqrt(20)), возвращает квадратный корень числа. То же самое относится и к классу System, в котором определен статический объект out, поэтому можно его использовать без указания класса, если выполнен статический импорт класса System.
Пример использования классов разных пакетов
Рассмотрим простенький проект PackageExample, включающий 3 java-класса. Два java-класса располагаются в одном пакете «ru.java.online», а третий — в другом «ru.java.online.classes». Структура проекта представлена на следующем скриншоте:
Листинг базового класса, BaseClass.java
Базовый класс включает 2 поля (id, name) и методы get/set. В конструкторе значения полей инициализируется.
package ru.java.online; public class BaseClass < private String protected String name = null; public BaseClass() < this.id = "default"; this.name = "Наименование не определено"; >public String getId() < return id; >public void setId(String id) < this.id = id; >public String getName() < return name; >public void setName(String name) < this.name = name; >@Override public String toString() < return this.getClass().getName() + "\n\t"; > >
Переопределенная функция toString() возвращает наименование класса и значение полей.
Листинг наследника, Inheritor.java
Класс Inheritor.java наследует свойства базового класса BaseClass.java. Поскольку классы располаются в разных пакетах, то базовый класс необходимо импортировать.
package ru.java.online.classes; import ru.java.online.BaseClass; public class Inheritor extends BaseClass < public Inheritor() < this.name = "Наследник"; this.setId("Первый"); >>
Листинг основного класса, MainPackage.java
Основной класс включает статический метод main. Поскольку данный класс не «видит» наследника, то его приходится импортировать.
package ru.java.online; import ru.java.online.classes.Inheritor; public class MainPackage < public MainPackage() < BaseClass bc = new BaseClass(); Inheritor ir = new Inheritor(); System.out.println (bc.toString()); System.out.println (ir.toString()); >public static void main(String[] args) < new MainPackage(); System.exit(0); >>
Результат выполнения данной программы выводится в консоль в следующем виде :
ru.java.online.BaseClass: ru.java.online.classes.Inheritor:
Как видно по результату выполнения программы наименование класса включает пакет.
Говоря о полном наименовании класса следует отметить, что оно включает не только наименование пакета и наименование класса, но также и класс-загрузчик classloader. Подробно о классах-загрузчиках можно почитать здесь.
Static Import Statements in Java
In Java, the import statements imports classes from packages, so they can be used in the current class without the package reference. Similarly, the static import statements import the static members of the a class and allows to be used without class reference.
1. Types of Static Import Statements
A static import declaration comes in two flavors:
- single-static import: imports one static member (field or method) from a class.
- static-import-on-demand: imports all static members from a class. These are denoted with an asterisk (*).
The general syntax of static import statements is as follows:
import static >.>.>; import static >.>.*; //On-demand
For example, we can print the messages in the standard output using the System.out.println() method. System is a class in java.lang package that has a static variable named out .
When we use System.out , we are referring to that static variable out of the System class. We can use a static import statement to import the out static variable from the System class as follows:
import static java.lang.System.out;
The code can now use the name out to mean System.out in the program. The compiler will use the static import declaration to resolve the name out to System.out .
public class StaticImportTest < public static void main(String[] args) < out.println("Hello World!"); >>
2. Rules for Static Imports
The following are some important rules about static import statements.
2.1. Specifically Imported Member Preceeds Wildcard Imported Member
If two static members with the same simple name are imported, one using single-static import declaration and the other using static-import-on-demand declaration, the one imported using single-static import declaration takes precedence.
Suppose there are two classes, package1.Class1 and package2.Class2 . Both classes have a static method called methodA . The following code will use package1.Class1.methodA() method because it is imported using the single-static import declaration:
import static package1.Class1.methodA; // Imports Class1.methodA() method import static package2.Class2.*; // Imports Class2.methodA() method too public class Test < public static void main(String[] args) < methodA(); // Class1.methodA() will be called >>
2.2. Static Member Names should be Unique
Using single-static-import declaration to import two static members with the same simple name is not allowed. The following static import declarations generate an error because both of them import the static member with the same simple name of methodA :
import static package1.Class1.methodA; import static package1.Class2.methodA; // An error
2.3. Static Member Defined in the Current Class Takes Precedence
If a static member is imported using a single-static import declaration and there exists a static member in the same class with the same name, the static member in the class is used.
package package1; public class A < public static void test() < System.out.println("package1.A.test()"); >>
package package2; import static package1.A.test; public class Test < public static void main(String[] args) < test(); //package2.Test.test() >public static void test() < System.out.println("package2.Test.test()"); >>
The static imports help us use simple names of static members to make the program simpler to write and read. These are more of a syntactical sugar rather than providing any execution benefits in runtime.