`
jacktanlikejava
  • 浏览: 8037 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

Java reflect vs. Qt Meta-Object

阅读更多

Java的反射机制被广泛的应用在当今各种流行的开源框架中。只要你打开spring,struts或是hibernate的源代码,就可以发现Java反射的身影。利用反射,我们可以在系统运行时通过字符串来获取各种对象的类型,然后通过类型我们可以实例化对象并调用对象的相关方法。当然这一切对于Java来说都是非常的简单和容易理解,因为Java有虚拟机的存在,这个运行时系统就好比一个对象类型数据库,我们只要提供查询条件,就可以返回给我们所需要的对象类型。如下Java代码展示如何应用Reflect特性:

package org.storevm;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

public class MyClass {

    private String name;

    public MyClass() {

    }

    public void say() {

        System.out.println(name);

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public static void main(String[] args) throws Exception {

        Class clazz = Class.forName("org.storevm.MyClass");

        Object obj = clazz.newInstance();

        Field field = clazz.getDeclaredField("name");

        field.setAccessible(true);

        field.set(obj, "jacktan");

        Method method = clazz.getMethod("say", new Class[] {});

        method.invoke(obj, new Object[] {});

    }

}

上述代码的功能是:

1. 用字符串“org.storevm.MyClass”获取类型对象;

2. 将类型对象实例化为MyClass对象;

3. 从类型对象中获取name属性并设置为可读写状态;

4. 设置name属性的值为“jacktan”字符串;

5. 从类型对象中获取say方法对象;

6. 调用MyClass对象的say方法;

使用Java的反射,我们可以在不引用MyClass对象实例的情况下完成对MyClass对象的所有功能操作。

C++中并没有很强的内省功能,不过Qt 框架为C++扩展了一套类似Java反射的强大类型内省机制,我们称之为“Meta-Object”。通过这套系统,我们也可以非常容易的实现上述Java中实现的功能。不过相比较,Qt C++的编码要复杂一些,代码的可读性也不是很好,对于Java开发人来说可能看的不太习惯。如下代码展示了Qt C++中实现内省的特性:

头文件:cresultset.h

#ifndef CRESULTSET_H

#define CRESULTSET_H

 

#include<QObject>

#include<QString>

 

class CResultSet:public QObject

{

    Q_OBJECT

    Q_PROPERTY(QString m_name READ getName WRITE setName)

public:

    Q_INVOKABLE explicit CResultSet(QObject *parent = 0);

    Q_INVOKABLE CResultSet(const CResultSet &); //重新实现拷贝构造函数


    Q_INVOKABLE void say();

 

    Q_INVOKABLE QString getName() const {return m_name;}

    Q_INVOKABLE void setName(const QString &name){m_name = name;}

 
    CResultSet& operator=(const CResultSet &); //重载赋值操作符

private:

    QString m_name;

};

#endif// CRESULTSET_H


要实现C++的内省,首先必须公有继承Qt的QObject类,然后使用Q_OBJECT宏来标识需要实现内省。如果不继承QObject类而直接使用Q_OBJECT宏,则编译报错。Q_PROPERTY宏用以标识可被内省的成员变量,括号内看上去非常奇怪写法(看到下面就知道为什么要如此的写法)定义了成员变量的类型,名称以及读写该成员变量的setter和getter函数。Q_INVOKABLE宏用以标识可被内省的成员函数,这里需要注意的是,我们在类的构造函数上也标识了Q_INVOKEABLE宏,之后就能看到这样做的原因。为了实现内省,这些宏的标识是必须的,如果不在成员变量或成员函数上使用这些宏,则在调用indexOfProperty和indexOfMethod等函数时将永远返回-1,即无法使用内省。相比Java的实现,Qt的宏更具操控性,控制粒度更加细致,但是增加了编码量。

另外要提一下继承QObject时需要关注的问题。在QObject类中,赋值操作符和拷贝构造函数被定义为私有的,也就是说,通过继承QObject的类是无法进行赋值操作和对象拷贝的。为了解决这个问题,我们必须重新实现拷贝构造函数并重载赋值操作符。

以下代码为cpp文件的内容:cresultset.cpp

#include "cresultset.h"

#include <QDebug>

 

CResultSet::CResultSet(QObject *parent) :

    QObject(parent)

{

}

 

//拷贝构造函数

CResultSet::CResultSet(const CResultSet &other)
{
    *this=other;

}

 

void CResultSet::say()
{

    qDebug() << this->m_name;

}

 

//赋值操作符重载
CResultSet& CResultSet::operator =(const CResultSet &other)

{
    this->m_name = other.m_name;

    return *this;

}

以下代码为Meta-Object特性的调用:main.cpp

#include <QtGui/QApplication>

#include <QDebug>

#include <QMetaObject>

#include <QMetaProperty>

 

int main(int argc,char *argv[])

{

    QApplicationa(argc, argv);

    QMetaObject metaObject = CResultSet::staticMetaObject;
    
    QObject* rs = metaObject.newInstance();   

    int pidx = (rs->metaObject())->indexOfProperty("m_name");

    qDebug() << pidx;

    QMetaProperty property = (rs->metaObject())->property(pidx);

    property.write(rs, QVariant("jacktan111"));

    QMetaObject::invokeMethod(rs, "say", Qt::DirectConnection);

    return a.exec();
}

首先使用类(此类必须继承自QObject)的静态函数staticMetaObject获取QMetaObject对象,该对象中包含了所有元对象信息,包括类名,成员函数,成员变量,信号和槽等,类似于Java中的Class类。然后通过QMetaObject对象的newInstance函数返回QObject的指针,这步类似Java中的Class.newInstance方法。接下去就是一些操作元对象的操作。比如通过实例对象的QMetaObject对象的indexOfProperty函数可以返回指定成员变量名称的索引值,比如利用该索引值调用QMetaObject::property(int)函数,就能得到QMetaProperty对象,该对象类似Java中的Field类,保存了有关于成员变量的相关元数据。QMetaProperty对象中read和write函数分别用于成员变量的读写。写到这里,相信大家都明白了吧!之前的Q_PROPERTY宏为什么定义成如此奇怪的形状。正是通过READ和WRITE指定的函数名称来实现QMetaProperty对象中的read和write函数。

使用QMetaObject对象的静态函数invokeMethod可以调用指定对象的成员函数,前提是被调用的成员函数必须被Q_INVOKABLE宏所修饰。成员函数的调用有2种方式:同步的和异步的,同步使用Qt:: DirectConnection参数,异步使用Qt:: QueuedConnection参数。上述C++代码的功能与Java版本实现的功能完全一样。即通过内省特性为一个实例化对象的成员变量赋值,并调用指定的成员函数。相比之下,Java的内省更容易理解且使用起来也更自然一些,至少没有那些让人看的很费解的宏定义。

最后说说Qt这个C++开发框架,它的强大自不必多说,时间证明了一切。尤其是其跨平台的能力绝对不亚于号称一次编写,随处运行的Java。只要做过跨平台开发的人都知道,要真正实现跨平台应用程序是何其的困难,光是编译源代码这一关就能彻底摧毁你对跨平台应用程序的美好期望,所以为什么Java会那么不厌其烦的宣扬它的跨平台能力。对于开发人员来说根本无需关心编写的代码将来要运行在什么平台上,但是Java的跨平台是借助了JVM的力量,JVM本身并不是跨平台的。每个平台有其自己特定的JVM实现,只是对开发人员来说这一切是透明的,无需开发人员关注。C++是编译成本地代码执行的,没有JVM这种just in time的运行时系统。所以从这点看Qt实现的跨平台比Java更强大,但是天下无免费晚餐,Qt为了实现跨平台,把所有的负担转嫁给了程序员,为了实现跨平台,程序员需要更加缜密的思维和使用大量令人费解的宏定义。

Java的弱势是其GUI方面,虽然现在基于浏览器的应用程序满天飞,但是GUI程序并没有因此而退出历史舞台,在很多场合GUI程序以其界面的统一性以及高性能被广泛的应用在如嵌入式系统,工业应用等领域。这也是QtGUI为什么能活跃到现在的原因。在某些企业应用中,Java和C++可以完美的组合起来,比如,Java用于编写业务逻辑组件,充分发挥其面向对象建模的优势。而C++ Qt GUI则可利用跨语言的通讯机制与Java服务器组件进行交互,从而实现高性能的用户界面体验。而在另外一些场合,我们则可以利用Qt C++的高性能编写服务器端业务处理,而使用Java编写B/S界面解决统一部署的问题,充分结合2种开发语言的优势。所以最为一个好的设计人员,不能拘泥于一种语言的实现平台,应该海纳百川,充分利用各种平台的优势加以结合,创建趋于完美的应用系统。

Qt在很多方面借鉴了Java的设计理念,比如Qt的GUI组件模型,Qt的内省,Qt的布局管理器,Qt的MVC模型等等,处处可见Java设计的影子。所以如果你是一个Java开发人员,那当你遇到Qt时,肯定会有一种似曾相识的感觉。


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics