Maison > Java > javaDidacticiel > Explication détaillée des dix principales nouvelles fonctionnalités de JAVA8

Explication détaillée des dix principales nouvelles fonctionnalités de JAVA8

高洛峰
Libérer: 2017-01-18 16:38:58
original
1313 Les gens l'ont consulté

"Java n'est toujours pas mort, et les gens commencent à le comprendre."

Ce didacticiel utilisera un code annoté simple pour décrire les nouvelles fonctionnalités, et vous ne verrez pas de gros blocs de texte effrayant.

1. Méthode par défaut de l'interface

Java 8 nous permet d'ajouter une implémentation de méthode non abstraite à l'interface. Il suffit d'utiliser le mot-clé par défaut. Cette fonctionnalité est également appelée extension. méthode.L'exemple est le suivant :

interface Formula {
    double calculate(int a);
    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}
Copier après la connexion

En plus de la méthode calculate, l'interface Formula définit également la méthode sqrt. Les sous-classes qui implémentent l'interface Formula doivent uniquement implémenter une méthode calculate. sqrt sera directement utilisé sur la sous-classe.

Formula formula = new Formula() {
    @Override
    public double calculate(int a) {
        return sqrt(a * 100);
    }
};
formula.calculate(100);     // 100.0
formula.sqrt(16);           // 4.0
Copier après la connexion

La formule de l'article est implémentée comme une instance d'une classe anonyme. Le code est très simple à comprendre. Le calcul de sqrt(a * 100) est implémenté en 6 lignes de code. Dans la section suivante, nous verrons une manière plus simple d’implémenter une interface à méthode unique.

Note du traducteur : il n'y a qu'un seul héritage en Java. Si vous souhaitez donner de nouvelles caractéristiques à une classe, elle est généralement implémentée à l'aide d'une interface. L'héritage multiple est pris en charge en C, permettant à une sous-classe d'avoir plusieurs parents. en même temps. Interfaces et fonctions des classes. Dans d'autres langages, la méthode permettant à une classe d'avoir d'autres codes réutilisables en même temps est appelée mixin. Cette fonctionnalité du nouveau Java 8 est plus proche du trait Scala du point de vue de l'implémentation du compilateur. Il existe également un concept appelé méthode d'extension en C#, qui permet d'étendre les méthodes aux types existants. Ceci est sémantiquement différent de Java 8.

2. Expression Lambda

Regardez d'abord comment les chaînes sont disposées dans les anciennes versions de Java :

List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});
Copier après la connexion

Donnez simplement la méthode statique Collections.sort Passer dans une liste objet et un comparateur pour trier dans l’ordre spécifié. L'approche habituelle consiste à créer un objet comparateur anonyme et à le transmettre à la méthode de tri.

Dans Java 8, vous n'avez plus besoin d'utiliser cette méthode d'objet anonyme traditionnelle. Java 8 fournit une syntaxe plus concise, une expression lambda :

Collections.sort(names, (String a, String b) -> {
    return b.compareTo(a);
});
Copier après la connexion

Vous le voyez, le code devient plus segmenté. et plus lisible, mais il peut en fait être écrit plus court :

Collections.sort(names, (String a, String b) -> b.compareTo(a));
Copier après la connexion

Pour un corps de fonction avec une seule ligne de code, vous pouvez supprimer les accolades {} et les mots clés de retour, mais vous pouvez aussi écrivez-le plus court :

Collections.sort(names, (a, b) -> b.compareTo(a));
Copier après la connexion

Le compilateur Java peut automatiquement déduire le type du paramètre, vous n'avez donc pas besoin de réécrire le type. Voyons ensuite quelles autres choses plus pratiques les expressions lambda peuvent faire :

3. Interface fonctionnelle

Comment les expressions lambda sont-elles représentées dans le système de types Java ? Chaque expression lambda correspond à un type, généralement un type d'interface. « Interface fonctionnelle » fait référence à une interface qui ne contient qu'une seule méthode abstraite. Chaque expression lambda de ce type sera mise en correspondance avec cette méthode abstraite. Étant donné que les méthodes par défaut ne sont pas considérées comme des méthodes abstraites, vous pouvez également ajouter des méthodes par défaut à votre interface fonctionnelle.


Nous pouvons traiter les expressions lambda comme n'importe quel type d'interface qui ne contient qu'une seule méthode abstraite. Pour garantir que votre interface doit répondre à cette exigence, il vous suffit d'ajouter l'annotation @FunctionalInterface, le compilateur signalera une erreur s'il constate que l'interface que vous avez annotée avec cette annotation possède plusieurs méthodes abstraites.

Un exemple est le suivant :

@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted);    // 123
Copier après la connexion

Il est à noter que si @FunctionalInterface n'est pas spécifié, le code ci-dessus est également correct.


Note du traducteur : mappage des expressions lambda sur une interface à méthode unique Cette approche a été implémentée dans d'autres langages avant Java 8, tels que l'interpréteur JavaScript Rhino, le cas échéant. Le paramètre function reçoit une interface à méthode unique et vous lui transmettez une fonction, l'interpréteur Rhino créera automatiquement un adaptateur à partir d'une instance à interface unique vers la fonction. Les scénarios d'application typiques incluent org.w3c.dom.events.EventTarget. de addEventListener est EventListener.

4. Références de méthode et de constructeur

Le code de la section précédente peut également être représenté par des références de méthode statiques :

Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted);   // 123
Copier après la connexion

Java 8 vous permet d'utiliser : Mot clé pour transmettre la référence d'une méthode ou d'un constructeur. Le code ci-dessus montre comment faire référence à une méthode statique. Nous pouvons également faire référence à la méthode d'un objet :

 converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted);    // "J"
Copier après la connexion

Voyons ensuite comment le constructeur est utilisé. :: Pour référencer par mot-clé, nous définissons d'abord une classe simple contenant plusieurs constructeurs :

class Person {
    String firstName;
    String lastName;
    Person() {}
    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}
Copier après la connexion

Ensuite, nous spécifions une interface de fabrique d'objets utilisée pour créer des objets Personne :

interface PersonFactory<P extends Person> {
    P create(String firstName, String lastName);
}
Copier après la connexion

Ici, nous utilisez des références de constructeur pour les associer au lieu d'implémenter une fabrique complète :

PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");
Copier après la connexion

Nous n'avons besoin que d'utiliser Person::new pour obtenir une référence au constructeur de la classe Person, compilation Java Le constructeur sélectionnera automatiquement le constructeur approprié constructeur basé sur la signature de la méthode PersonFactory.create.

5. Portée Lambda

La manière d'accéder à la portée externe dans l'expression lambda est très similaire à celle de l'ancienne version de l'objet anonyme. Vous pouvez accéder directement aux variables locales externes marquées comme finales, ou aux champs d'instance et aux variables statiques.

6. Accéder aux variables locales

On peut accéder directement aux variables locales externes dans les expressions lambda :

final int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);
stringConverter.convert(2);     // 3
Copier après la connexion

Mais contrairement aux objets anonymes, ici La variable num n'a pas besoin à déclarer final. Le code est également correct :

int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);
stringConverter.convert(2);     // 3
Copier après la connexion

Cependant, le num ici ne doit pas être modifié par le code ultérieur (c'est-à-dire qu'il a une sémantique finale implicite). Par exemple, ce qui suit ne peut pas être compilé. :

int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);
num = 3;
Copier après la connexion

在lambda表达式中试图修改num同样是不允许的。

七、访问对象字段与静态变量

和本地变量不同的是,lambda内部对于实例的字段以及静态变量是即可读又可写。该行为和匿名对象是一致的:

class Lambda4 {
    static int outerStaticNum;
    int outerNum;
    void testScopes() {
        Converter<Integer, String> stringConverter1 = (from) -> {
            outerNum = 23;
            return String.valueOf(from);
        };
        Converter<Integer, String> stringConverter2 = (from) -> {
            outerStaticNum = 72;
            return String.valueOf(from);
        };
    }
}
Copier après la connexion

八、访问接口的默认方法

还记得第一节中的formula例子么,接口Formula定义了一个默认方法sqrt可以直接被formula的实例包括匿名对象访问到,但是在lambda表达式中这个是不行的。
Lambda表达式中是无法访问到默认方法的,以下代码将无法编译:

Formula formula = (a) -> sqrt( a * 100);
Built-in Functional Interfaces
Copier après la connexion

JDK 1.8 API包含了很多内建的函数式接口,在老Java中常用到的比如Comparator或者Runnable接口,这些接口都增加了@FunctionalInterface注解以便能用在lambda上。
Java 8 API同样还提供了很多全新的函数式接口来让工作更加方便,有一些接口是来自Google Guava库里的,即便你对这些很熟悉了,还是有必要看看这些是如何扩展到lambda上使用的。

Predicate接口


Predicate 接口只有一个参数,返回boolean类型。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非):

Predicate<String> predicate = (s) -> s.length() > 0;
predicate.test("foo");              // true
predicate.negate().test("foo");     // false
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
Copier après la connexion

Function 接口

Function 接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen):

Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);
backToString.apply("123");     // "123"
Copier après la connexion

Supplier 接口

Supplier 接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数

Supplier<Person> personSupplier = Person::new;
personSupplier.get();   // new Person
Copier après la connexion

Consumer 接口

Consumer 接口表示执行在单个参数上的操作。

Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));
Copier après la connexion

Comparator 接口

Comparator 是老Java中的经典接口, Java 8在此之上添加了多种默认方法:

Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
Person p1 = new Person("John", "Doe");
Person p2 = new Person("Alice", "Wonderland");
comparator.compare(p1, p2);             // > 0
comparator.reversed().compare(p1, p2);  // < 0
Copier après la connexion

Optional 接口

Optional 不是函数是接口,这是个用来防止NullPointerException异常的辅助类型,这是下一届中将要用到的重要概念,现在先简单的看看这个接口能干什么:

Optional 被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8中,不推荐你返回null而是返回Optional。

Optional<String> optional = Optional.of("bam");
optional.isPresent();           // true
optional.get();                 // "bam"
optional.orElse("fallback");    // "bam"
optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"
Copier après la connexion

Stream 接口

java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。Stream的操作可以串行执行或者并行执行。

首先看看Stream是怎么用,首先创建实例代码的用到的数据List:

List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
Copier après la connexion

Java 8扩展了集合类,可以通过 Collection.stream() 或者 Collection.parallelStream() 来创建一个Stream。下面几节将详细解释常用的Stream操作:

Filter 过滤

过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。

stringCollection
    .stream()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);
// "aaa2", "aaa1"
Copier après la connexion

Sort 排序

排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。

stringCollection
    .stream()
    .sorted()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);
// "aaa1", "aaa2"
Copier après la connexion

需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据stringCollection是不会被修改的:

System.out.println(stringCollection);
// ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1
Copier après la connexion

Map 映射

中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。

stringCollection
    .stream()
    .map(String::toUpperCase)
    .sorted((a, b) -> b.compareTo(a))
    .forEach(System.out::println);
// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
Copier après la connexion
Match 匹配

Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。

boolean anyStartsWithA = 
    stringCollection
        .stream()
        .anyMatch((s) -> s.startsWith("a"));
System.out.println(anyStartsWithA);      // true
boolean allStartsWithA = 
    stringCollection
        .stream()
        .allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA);      // false
boolean noneStartsWithZ = 
    stringCollection
        .stream()
        .noneMatch((s) -> s.startsWith("z"));
System.out.println(noneStartsWithZ);      // true
Copier après la connexion

Count 计数

计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。

long startsWithB = 
    stringCollection
        .stream()
        .filter((s) -> s.startsWith("b"))
        .count();
System.out.println(startsWithB);    // 3
Copier après la connexion

Reduce 规约

这是一个最终操作,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规越后的结果是通过Optional接口表示的:

Optional<String> reduced =
    stringCollection
        .stream()
        .sorted()
        .reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
Copier après la connexion

并行Streams

前面提到过Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。

下面的例子展示了是如何通过并行Stream来提升性能:

首先我们创建一个没有重复元素的大表:

int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
    UUID uuid = UUID.randomUUID();
    values.add(uuid.toString());
}
Copier après la connexion

然后我们计算一下排序这个Stream要耗时多久,
串行排序:

long t0 = System.nanoTime();
long count = values.stream().sorted().count();
System.out.println(count);
long t1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("sequential sort took: %d ms", millis));
Copier après la connexion

// 串行耗时: 899 ms
并行排序:

long t0 = System.nanoTime();
long count = values.parallelStream().sorted().count();
System.out.println(count);
long t1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("parallel sort took: %d ms", millis));
Copier après la connexion

// 并行排序耗时: 472 ms
上面两个代码几乎是一样的,但是并行版的快了50%之多,唯一需要做的改动就是将stream()改为parallelStream()。

Map

前面提到过,Map类型不支持stream,不过Map提供了一些新的有用的方法来处理一些日常任务。

Map<Integer, String> map = new HashMap<>();
for (int i = 0; i < 10; i++) {
    map.putIfAbsent(i, "val" + i);
}
Copier après la connexion

map.forEach((id, val) -> System.out.println(val));
以上代码很容易理解, putIfAbsent 不需要我们做额外的存在性检查,而forEach则接收一个Consumer接口来对map里的每一个键值对进行操作。

下面的例子展示了map上的其他有用的函数:

map.computeIfPresent(3, (num, val) -> val + num);
map.get(3);             // val33
map.computeIfPresent(9, (num, val) -> null);
map.containsKey(9);     // false
map.computeIfAbsent(23, num -> "val" + num);
map.containsKey(23);    // true
map.computeIfAbsent(3, num -> "bam");
map.get(3);             // val33
Copier après la connexion

接下来展示如何在Map里删除一个键值全都匹配的项:

map.remove(3, "val3");
map.get(3);             // val33
map.remove(3, "val33");
map.get(3);             // null
Copier après la connexion

另外一个有用的方法:

map.getOrDefault(42, "not found");  // not found
Copier après la connexion

对Map的元素做合并也变得很容易了:

map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9
map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9concat
Copier après la connexion

Merge做的事情是如果键名不存在则插入,否则则对原键对应的值做合并操作并重新插入到map中。

九、Date API

Java 8 在包java.time下包含了一组全新的时间日期API。新的日期API和开源的Joda-Time库差不多,但又不完全一样,下面的例子展示了这组新API里最重要的一些部分:

Clock 时钟

Clock类提供了访问当前日期和时间的方法,Clock是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数。某一个特定的时间点也可以使用Instant类来表示,Instant类也可以用来创建老的java.util.Date对象。

Clock clock = Clock.systemDefaultZone();
long millis = clock.millis();
Instant instant = clock.instant();
Date legacyDate = Date.from(instant);   // legacy java.util.Date
Copier après la connexion

Timezones 时区

在新API中时区使用ZoneId来表示。时区可以很方便的使用静态方法of来获取到。 时区定义了到UTS时间的时间差,在Instant时间点对象到本地日期对象之间转换的时候是极其重要的。

System.out.println(ZoneId.getAvailableZoneIds());
// prints all available timezone ids
ZoneId zone1 = ZoneId.of("Europe/Berlin");
ZoneId zone2 = ZoneId.of("Brazil/East");
System.out.println(zone1.getRules());
System.out.println(zone2.getRules());
// ZoneRules[currentStandardOffset=+01:00]
// ZoneRules[currentStandardOffset=-03:00]
Copier après la connexion

LocalTime 本地时间

LocalTime 定义了一个没有时区信息的时间,例如 晚上10点,或者 17:30:15。下面的例子使用前面代码创建的时区创建了两个本地时间。之后比较时间并以小时和分钟为单位计算两个时间的时间差:

LocalTime now1 = LocalTime.now(zone1);
LocalTime now2 = LocalTime.now(zone2);
System.out.println(now1.isBefore(now2));  // false
long hoursBetween = ChronoUnit.HOURS.between(now1, now2);
long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);
System.out.println(hoursBetween);       // -3
System.out.println(minutesBetween);     // -239
Copier après la connexion

LocalTime 提供了多种工厂方法来简化对象的创建,包括解析时间字符串。

LocalTime late = LocalTime.of(23, 59, 59);
System.out.println(late);       // 23:59:59
DateTimeFormatter germanFormatter =
    DateTimeFormatter
        .ofLocalizedTime(FormatStyle.SHORT)
        .withLocale(Locale.GERMAN);
LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);
System.out.println(leetTime);   // 13:37
Copier après la connexion

LocalDate 本地日期

LocalDate 表示了一个确切的日期,比如 2014-03-11。该对象值是不可变的,用起来和LocalTime基本一致。下面的例子展示了如何给Date对象加减天/月/年。另外要注意的是这些对象是不可变的,操作返回的总是一个新实例。

LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);
LocalDate yesterday = tomorrow.minusDays(2);
LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);
DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();
Copier après la connexion

System.out.println(dayOfWeek); // FRIDAY
从字符串解析一个LocalDate类型和解析LocalTime一样简单:

DateTimeFormatter germanFormatter =
    DateTimeFormatter
        .ofLocalizedDate(FormatStyle.MEDIUM)
        .withLocale(Locale.GERMAN);
LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);
System.out.println(xmas);   // 2014-12-24
Copier après la connexion

LocalDateTime 本地日期时间

LocalDateTime 同时表示了时间和日期,相当于前两节内容合并到一个对象上了。LocalDateTime和LocalTime还有LocalDate一样,都是不可变的。LocalDateTime提供了一些能访问具体字段的方法。

LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);
DayOfWeek dayOfWeek = sylvester.getDayOfWeek();
System.out.println(dayOfWeek);      // WEDNESDAY
Month month = sylvester.getMonth();
System.out.println(month);          // DECEMBER
long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);
System.out.println(minuteOfDay);    // 1439
Copier après la connexion

只要附加上时区信息,就可以将其转换为一个时间点Instant对象,Instant时间点对象可以很容易的转换为老式的java.util.Date。

Instant instant = sylvester
        .atZone(ZoneId.systemDefault())
        .toInstant();
Date legacyDate = Date.from(instant);
System.out.println(legacyDate);     // Wed Dec 31 23:59:59 CET 2014
Copier après la connexion

格式化LocalDateTime和格式化时间和日期一样的,除了使用预定义好的格式外,我们也可以自己定义格式:

DateTimeFormatter formatter =
    DateTimeFormatter
        .ofPattern("MMM dd, yyyy - HH:mm");
LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);
String string = formatter.format(parsed);
System.out.println(string);     // Nov 03, 2014 - 07:13
Copier après la connexion

和java.text.NumberFormat不一样的是新版的DateTimeFormatter是不可变的,所以它是线程安全的。
关于时间日期格式的详细信息:http://download.java.net/jdk8/docs/api/java/time/format/DateTimeFormatter.html


十、Annotation 注解

在Java 8中支持多重注解了,先看个例子来理解一下是什么意思。
首先定义一个包装类Hints注解用来放置一组具体的Hint注解:

@interface Hints {
    Hint[] value();
}
@Repeatable(Hints.class)
@interface Hint {
    String value();
}
Copier après la connexion

Java 8允许我们把同一个类型的注解使用多次,只需要给该注解标注一下@Repeatable即可。

例 1: 使用包装类当容器来存多个注解(老方法)

@Hints({@Hint("hint1"), @Hint("hint2")})
class Person {}
Copier après la connexion

例 2:使用多重注解(新方法)

@Hint("hint1")
@Hint("hint2")
class Person {}
Copier après la connexion

第二个例子里java编译器会隐性的帮你定义好@Hints注解,了解这一点有助于你用反射来获取这些信息:

Hint hint = Person.class.getAnnotation(Hint.class);
System.out.println(hint);                   // null
Hints hints1 = Person.class.getAnnotation(Hints.class);
System.out.println(hints1.value().length);  // 2
Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);
System.out.println(hints2.length);          // 2
Copier après la connexion

即便我们没有在Person类上定义@Hints注解,我们还是可以通过 getAnnotation(Hints.class) 来获取 @Hints注解,更加方便的方法是使用 getAnnotationsByType 可以直接获取到所有的@Hint注解。
另外Java 8的注解还增加到两种新的target上了:

@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@interface MyAnnotation {}
Copier après la connexion

关于Java 8的新特性就写到这了,肯定还有更多的特性等待发掘。JDK 1.8里还有很多很有用的东西,比如Arrays.parallelSort, StampedLock和CompletableFuture等等。

更多JAVA8 十大新特性详解相关文章请关注PHP中文网!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal