Перекрестные типы позволяют, Вы к (своего рода вид) делаете перечисления, которые имеют иерархию наследования. Вы не можете наследовать реализацию, но можно делегировать ее к классу помощника.
enum Foo1 implements Bar {}
enum Foo2 implements Bar {}
class HelperClass {
static <T extends Enum<T> & Bar> void fooBar(T the enum) {}
}
Это полезно, когда у Вас есть много различных перечислений, которые реализуют своего рода шаблон. Например, много пар перечислений, которые имеют отношения отцов и детей.
enum PrimaryColor {Red, Green, Blue;}
enum PastelColor {Pink, HotPink, Rockmelon, SkyBlue, BabyBlue;}
enum TransportMedium {Land, Sea, Air;}
enum Vehicle {Car, Truck, BigBoat, LittleBoat, JetFighter, HotAirBaloon;}
можно ли записать общие методы, которые говорят "Хорошо, учитывая перечисление значений, это - родитель некоторых других перечислимых значений, какой процент всех возможных дочерних перечислений дочернего типа имеют это конкретное родительское значение как их родителя?", и имейте все это безопасное с точки зрения типов и обошедшееся без кастинг. (например: то "Море" составляет 33% всех возможных механизмов и "Зеленые" 20% всех возможных Пастелей).
код похожи на это. Это довольно противно, но существуют способы сделать его лучше. Обратите внимание в particuar, что сами "листовые" классы довольно аккуратны - универсальные классы имеют объявления, которые ужасно ужасны, но Вы только пишете им onece. После того как универсальные классы там, затем используют их, легко.
import java.util.EnumSet;
import javax.swing.JComponent;
public class zz extends JComponent {
public static void main(String[] args) {
System.out.println(PrimaryColor.Green + " " + ParentUtil.pctOf(PrimaryColor.Green) + "%");
System.out.println(TransportMedium.Air + " " + ParentUtil.pctOf(TransportMedium.Air) + "%");
}
}
class ParentUtil {
private ParentUtil(){}
static <P extends Enum<P> & Parent<P, C>, C extends Enum<C> & Child<P, C>> //
float pctOf(P parent) {
return (float) parent.getChildren().size() / //
(float) EnumSet.allOf(parent.getChildClass()).size() //
* 100f;
}
public static <P extends Enum<P> & Parent<P, C>, C extends Enum<C> & Child<P, C>> //
EnumSet<C> loadChildrenOf(P p) {
EnumSet<C> cc = EnumSet.noneOf(p.getChildClass());
for(C c: EnumSet.allOf(p.getChildClass())) {
if(c.getParent() == p) {
cc.add(c);
}
}
return cc;
}
}
interface Parent<P extends Enum<P> & Parent<P, C>, C extends Enum<C> & Child<P, C>> {
Class<C> getChildClass();
EnumSet<C> getChildren();
}
interface Child<P extends Enum<P> & Parent<P, C>, C extends Enum<C> & Child<P, C>> {
Class<P> getParentClass();
P getParent();
}
enum PrimaryColor implements Parent<PrimaryColor, PastelColor> {
Red, Green, Blue;
private EnumSet<PastelColor> children;
public Class<PastelColor> getChildClass() {
return PastelColor.class;
}
public EnumSet<PastelColor> getChildren() {
if(children == null) children=ParentUtil.loadChildrenOf(this);
return children;
}
}
enum PastelColor implements Child<PrimaryColor, PastelColor> {
Pink(PrimaryColor.Red), HotPink(PrimaryColor.Red), //
Rockmelon(PrimaryColor.Green), //
SkyBlue(PrimaryColor.Blue), BabyBlue(PrimaryColor.Blue);
final PrimaryColor parent;
private PastelColor(PrimaryColor parent) {
this.parent = parent;
}
public Class<PrimaryColor> getParentClass() {
return PrimaryColor.class;
}
public PrimaryColor getParent() {
return parent;
}
}
enum TransportMedium implements Parent<TransportMedium, Vehicle> {
Land, Sea, Air;
private EnumSet<Vehicle> children;
public Class<Vehicle> getChildClass() {
return Vehicle.class;
}
public EnumSet<Vehicle> getChildren() {
if(children == null) children=ParentUtil.loadChildrenOf(this);
return children;
}
}
enum Vehicle implements Child<TransportMedium, Vehicle> {
Car(TransportMedium.Land), Truck(TransportMedium.Land), //
BigBoat(TransportMedium.Sea), LittleBoat(TransportMedium.Sea), //
JetFighter(TransportMedium.Air), HotAirBaloon(TransportMedium.Air);
private final TransportMedium parent;
private Vehicle(TransportMedium parent) {
this.parent = parent;
}
public Class<TransportMedium> getParentClass() {
return TransportMedium.class;
}
public TransportMedium getParent() {
return parent;
}
}
## объединяет два токена вместе. Его можно использовать только в препроцессоре.
f (1,2)
становится 1 ## 2
становится 12
.
Сам по себе оператор # преобразует токенов в строку: #a
становится «a»
. Следовательно, g (f (1,2))
становится «f (1,2)»
, когда препроцессор завершает работу с ним.
h (f (1,2) ))
фактически # (1 ## 2)
, который становится # 12
, который становится "12"
, когда препроцессор работает над ним.
По таким вопросам (а также к другим «реальным» проблемам, связанным с препроцессором), я считаю очень полезным прочитать код, после он был предварительно обработан.
Как это сделать зависит от компилятора, но с gcc вы должны использовать это:
$ gcc -E test.c
(snip)
main()
{
printf("%s\n","12");
printf("%s\n","f(1,2)");
}
Итак, вы можете видеть, что символы были объединены и преобразованы в строку.
Макрос f (a, b) объединяет свои аргументы, g (a) превращает свои аргументы в строку, а h (a) является вспомогательным макросом для g (a). Я думаю, он выведет:
12
f(1,2)
Причина в том, что макрос h (a) вызывает полное раскрытие своего аргумента перед передачей его в g (a), тогда как g (a) принимает свои аргументы буквально, не раскрывая их предварительно.
a##b will paste the code togather.
so f(1,2) will become 12
a ## b - это строковое соединение литералов a и b, поэтому f (1,2) равно "12"
#a - это строковый литерал a, поэтому g (3 ) равно "3"
## - это оператор конкатенации макросов. Так, например, f (foo,
#define f(a,b) a##b #define g(a) #a #define h(a) g(a)
Итак, ## объединяет две части напрямую, независимо от того, какого они типа являются...
Приведу пример ..
printf ("% d \ n", f (1,2));
вы получите 12, это означает, что здесь f (1,2) равно 12 целому числу.
int a2 = 100; printf("%d\n",f(a,2));
здесь f (a, 2 ) это метка. он указывает на метку в контексте вашего кода, если нет int a2 = 100
, вы получите ошибки компиляции.
И #a
превращает все, что есть, в строку ...
А затем h (a) g (a)
Это очень странно ..
Похоже, что когда вы вызываете h (a), он превращается в g (a) и передает a в g (a), во-первых, он интерпретирует, что такое a. Итак, прежде чем вы сможете g (a), a преобразуется в f (a, b) = a ## b = 12