Java和C#中的自定义元数据

Java的annotation和C#的Attribute,可用来为语言增加语义,定义元数据。

转自:http://rednaxelafx.iteye.com/blog/464889

http://blog.sina.com.cn/s/blog_69a4df530100p8w3.html

Java与C#都从一开始就强调程序的模块化,所以写出来的程序不但包括代码逻辑,还包括类型信息等“元数据”。Java早期版本只支持有限的几种元数据,用户无法自定义新的元数据类型;后来者C#则从一开始就在支持内建attribute的同时支持用户定义的attribute,为程序在运行时提供更多信息。从Java 5开始,Java添加了一些内建元数据类型,并且开始以annotation的形式支持用户定义的元数据。这样,在Java和C#中,用户都可以通过元数据来扩展语言,为语言提供更丰富的语义。

C#里要自定义attribute类型,可以直接或间接继承System.Attribute类,并通过AttributeUsageAttribute来指定attribute的应用范围,然后像定义普通的public类一样定义attribute的内容。 
指定应用范围的AttributeTargets有以下成员:

MSDN 写道

Assembly Attribute can be applied to an assembly.
Module Attribute can be applied to a module.
Class Attribute can be applied to a class.
Struct Attribute can be applied to a structure; that is, a value type.
Enum Attribute can be applied to an enumeration.
Constructor Attribute can be applied to a constructor.
Method Attribute can be applied to a method.
Property Attribute can be applied to a property.
Field Attribute can be applied to a field.
Event Attribute can be applied to an event.
Interface Attribute can be applied to an interface.
Parameter Attribute can be applied to a parameter.
Delegate Attribute can be applied to a delegate.
ReturnValue Attribute can be applied to a return value.
GenericParameter Attribute can be applied to a generic parameter.
All Attribute can be applied to any application element.

一个简单的attribute的例子:

C#代码  

  1. using System;
  2. // define a custom attribute
  3. [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
  4. public class LazyPopulateAttribute : Attribute {
  5. Type _propType;
  6. public LazyPopulateAttribute(Type propType) {
  7. _propType = propType;
  8. }
  9. public Type PropertyType {
  10. get { return _propType; }
  11. }
  12. }
  13. public class Singleton {
  14. static Singleton _instance;
  15. private Singleton() { }
  16. // use the custom attribute
  17. [LazyPopulate(typeof(Singleton))]
  18. public static Singleton Instance {
  19. get { return _instance; }
  20. }
  21. }
  22. static class Program {
  23. static void Main(string[] args) {
  24. var instance = Singleton.Instance;
  25. // ...
  26. }
  27. }

上面的代码光是这么写的话,_instance没人赋过值,用起来显然有问题。但我们可以写一个程序在postbuild时分析程序集,提取出其中的attribute,并且让LazyPopulateAttribute指定的属性展开为典型的double-check初始化,Instance展开后应该变为:

C#代码  

  1. public static Singleton Instance {
  2. get {
  3. var instance = _instance;
  4. if (null == instance) {
  5. lock(_lockObj) { // _lockObj是应该生成的成员
  6. if (null == _instance) {
  7. _instance = instance = new Singleton();
  8. }
  9. }
  10. }
  11. return instance;
  12. }
  13. }

在运行时通过反射,PropertyInfo.GetCustomAttributes()就可以得到针对property的attribute信息。具体要如何实现这个例子里的postbuild处理,下一篇再写。

Java方面,支持3种内建的annotation,包括@Override、@Deprecated、@SuppressWarnings。 
指定应用范围的ElementType枚举类型有以下成员:

Javadoc 写道

ANNOTATION_TYPE Annotation type declaration
CONSTRUCTOR Constructor declaration
FIELD Field declaration (includes enum constants)
LOCAL_VARIABLE Local variable declaration
METHOD Method declaration
PACKAGE Package declaration
PARAMETER Parameter declaration
TYPE Class, interface (including annotation type), or enum declaration

在Java中自定义annotation类型的方法很简单,跟定义接口类似。使用@interface关键字来声明annotation类型,然后像声明方法一样声明其中的成员。与定义接口不同的是,annotation类型的成员可以带有default默认值声明。

Java代码  

  1. public @interface NotImplemented {
  2. public enum Severity { CRITICAL, HIGH, MEDIUM, LOW, NONE }
  3. Severity severity() default Severity.NONE;
  4. }

要说C# attribute跟Java annotation有什么关系,相同点是它们都是元数据的载体,差别恐怕主要在于两者的可应用范围不同了。可以看到,两者受到C#和Java语言本身的差异的影响,可应用的范围已经有所不同,例如C#可以对程序集或者模块应用attribute,但不能对命名空间应用;Java可以对包应用annotation,但不能对例如说JAR文件之类的不属于Java语言本身所支持的组织范围应用。 
除去语言差异的影响,Java annotation有一个显著的特点就是它可以限定在别的annotation类型的定义上应用,也就是@Target({ElementType.ATTRIBUTE})。Java的@interface声明是extends java.lang.annotation.Annotation的语法糖,所以有@Target({ElementType.TYPE})的annotation也可以应用在annotation类型的定义上。在C#中,有[AttributeUsage(AttributeTarget.Class)]的attribute可以应用在任意类的定义上,而attribute本身就是个普通的类,所以自然也可以应用。不过在C#里无法直接定义出只能作用于attribute类型定义上的attribute,也就是说没有@Target({ElementType.ATTRIBUTE})的对应物,因而无法直接定义出专用的meta-annotation。这应该算是两者最大的区别了吧? 
还有一个差异:C#的attribute总是能带到运行时的;Java的annotation则可以选择RetentionPolicy,可以是CLASS、RUNTIME或者SOURCE,所以不一定会带到运行时,甚至不一定会带到编译出来的.class文件里。javac可以挂上一些钩子,可以将annotation处理直接挂在编译过程中,所以SOURCE级的annotation很有意义。Project Lombok把这种能力发挥到了极致……最近不停看到它的消息,但一直没深入调查,真是罪过啊 =v=

不知道今天被问孤城的问题这样解答算不算对呢……? 
说真的,上面提到的两个差异里,第二个我是前几天才刚看了一次的,不过被问的时候没想起来 T T 而第一个差异我一直没从“限制”的角度看,如果从“所有可应用范围”来看的话,我觉得还是C#的attribute范围大些,所以也是这么回答的。仔细想想,其实很难说谁大谁小,只能说设计得比较不同……

Java自定义注解小结

作者:谢伟伦

学习java有两年之余了,在很久之前,已经有位系统分析师告诉我,学习java,看java编程思想就够了。其言下之意,就是说,任何一切java框架,都是浮云,只有精通核心java,才是王道!

现在回想起来,这句话真的十分正确,一年多的工作,都只徘徊于学习java框架,造成自己是一个只会使用,不能原理的人。

趁着现在工作不忙,静下心来,好好学习一下核心java的基础。

今天早上,学习了java注解,这家伙以前经常用,但就是不知道其实现过程是如何,现在总算有所体会。

在一个java Project里,新建了三个类,其中有一个是java注解类,它们的源码如下:

package comm.annotation.test;

import java.lang.annotation.Documented;

import java.lang.annotation.ElementType;

import java.lang.annotation.Inherited;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

public @interface Description {

String author() default "william"; //定义"作者"参数

int size(); //定义"大小"参数

}

package comm.annotation.test;

@Description(author="helloworld",size=200)

public class DescriptionTest {

@SuppressWarnings(value="unchecked")

public static void main(String[] args) {

Class clazz = DescriptionTest.class;

if(clazz.isAnnotationPresent(Description.class)){

Description desc = (Description)clazz.getAnnotation(Description.class);

System.out.println("desc.author:" + desc.author());

System.out.println("desc.size:" + desc.size());

}else{

System.out.println("没有在DescriptionTest上使用注解!");

}

}

}

package comm.annotation.test;

public class SubDescriptionTest extends DescriptionTest{

@SuppressWarnings(value="unchecked")

public static void main(String[] args) {

Class clazz = DescriptionTest.class;

if(clazz.isAnnotationPresent(Description.class)){

Description desc = (Description)clazz.getAnnotation(Description.class);

System.out.println("desc.author:" + desc.author());

System.out.println("desc.size:" + desc.size());

}else{

System.out.println("没有在subDescriptionTest上使用注解!");

}

}

}

运行DescirptionTest和SubDescriptionTest两个java class,其输出的结果如下:

desc.author:helloworld

desc.size:200

我想说有三点:

(1)   java annotation的基础语法

(2)   java annotation的继承

(3)   关于对基于annotation框架的思考

第一点,        在源码里面,都说得十分简明了。更详尽的信息,请google一下。

第二点,          想的话就是“annotation与annotation之间没有继承关系”,只能用在类或接口里,由于在annotation类Description里面增加了@Inherited,因此,SubDescriptionTest直接拥有DescriptionTest其注解。

第三点,          在我古老的做法里,都用xml配置文件来让框架加载信息的,现在人们已经慢慢转向基于annotation方式来开发项目了,个人认为,利用java反射api,从注解处获得足够信息后,即可生成对象,与基于xml的配置效果是一样的。

接下来的时间,希望学习泛型这家伙,不能只看到外皮了。

时间: 2024-11-06 18:24:47

Java和C#中的自定义元数据的相关文章

java配置ueditor中解决“未找到上传文件”错误提示

ueditor是一个功能十分强大的在线文本编辑器,但是在ssh框架中,确切的说实在struts2中由于其拦截器需要对request,session对象进行重新封装,这个过程中会把request对象中保存的一些内容清空,所以会导致ueditor的上传功能获取不到需要上传的内容导致“未找到上传文件”的错误! 参考网上资料和自己实验,最终的解决思路是,重写struts2中的一个转换的类,然后配置struts2使用我们重写的这个类.由于我们的工程中可能会有其他的上传等功能,为了不影响其他功能的时候,还需

Java企业微信开发_04_自定义菜单

一.本节要点 1.菜单相关实体类的封装 参考官方文档中的请求包的内容,对菜单相关实体类进行封装. 这里需要格外注意的是,企业微信中请求包的数据是Json字符串格式的,而不是xml格式.关于json序列化的问题请参考上一节   Java企业微信开发_03_通讯录同步 2.创建菜单的接口 public static String create_menu_url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS

结合手机上网流量业务来说明Hadoop中的自定义数据类型(序列化、反序列化机制)

大家都知道,Hadoop中为Key的数据类型必须实现WritableComparable接口,而Value的数据类型只需要实现Writable接口即可:能做Key的一定可以做Value,能做Value的未必能做Key.但是具体应该怎么应用呢?--本篇文章将结合手机上网流量业务进行分析. 先介绍一下业务场景:统计每个用户的上行流量和,下行流量和,以及总流量和. 本次描述所用数据: 日志格式描述: 日志flowdata.txt中的具体数据: 接下来贴出详细代码,代码中含有详细注释,从代码中可以看出,

Java Web应用中自动实时检测资源文件内容变化

在Java Web应用中,我们经常需要配置文件来定制系统行为,这些配置文件可能包括:类路径下的文件和文件夹.非类路径下的绝对路径和相对路径的文件和文件夹,在分布式环境中,还需要通过HTTP从统一集中的Web服务器中获得配置信息,如何对这些配置信息进行自动加载并实时检测变化呢? Java分布式中文分词组件 - word分词已经实现了这个功能,我们看看是如何实现的: package org.apdplat.word.util; import java.io.BufferedReader; impor

Android中View自定义XML属性详解以及R.attr与R.styleable的区别

为View添加自定义XML属性 Android中的各种Widget都提供了很多XML属性,我们可以利用这些XML属性在layout文件中为Widget的属性赋值. 如下所示: <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> 我们可以通过TextView所提供

Android 中使用自定义字体的方法

1.Android系统默认支持三种字体,分别为:“sans”, “serif”, “monospace 2.在Android中可以引入其他字体 . <?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:Android="http://schemas.android.com/apk/res/android" Android:layout_width="fill

向Java枚举类型中添加新方法

除了不能继承enum之外,可将其看做一个常规类.甚至可以有main方法. 注意:必须先定义enum实例,实例的最后有一个分号. 下面是一个例子:返回对实例自身的描述,而非默认的toString返回枚举实例的名字. public enum Color { RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4); // 成员变量 private String nam

动态代理中的自定义注解样式

动态代理中的自定义注解的样式 @Target(ElementType.METHOD) 代表此注解使用对象是method@Retention(RetentionPolicy.RUNTIME) 代表此注解的生存域是运行期 package cn.tedu.anno; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPo

FineReport中如何自定义登录界面

在登录平台时,不希望使用FR默认的内置登录界面,想通过自定义登录界面实现登录操作,内置登录界面如下图: 登录界面,获取到用户名和密码的值,发送到报表系统,报表服务带着这两个参数访问认证地址进行认证. 自定义登录界面 登录界面设置 自定义html登录页面:命名为login.html,并保存在%FR_HOME%\WebReport下,代码如下: <html>   <head>  <meta http-equiv="Content-Type" content=&